Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0c99b791 authored by Nate Myren's avatar Nate Myren
Browse files

Check all tasks for good intent launch when creating channels

When creating a notification channel, call into the ActivityTaskManager
to determine if there are any valid tasks to show a permission prompt
over. Creates the method in ActivityTaskManagerInternal to support this.

Bug: 17047250
Test: atest NotificationPermissionTest
Change-Id: I7b2b6a42101607b1eaa25f108760cc2449661499
parent 8da49290
Loading
Loading
Loading
Loading
+5 −13
Original line number Diff line number Diff line
@@ -3746,6 +3746,10 @@ public class NotificationManagerService extends SystemService {
                    if (!hadChannel && hasChannel && !hasRequestedNotificationPermission
                            && startingTaskId != ActivityTaskManager.INVALID_TASK_ID) {
                        hasRequestedNotificationPermission = true;
                        if (mPermissionPolicyInternal == null) {
                            mPermissionPolicyInternal =
                                    LocalServices.getService(PermissionPolicyInternal.class);
                        }
                        mHandler.post(new ShowNotificationPermissionPromptRunnable(pkg,
                                UserHandle.getUserId(uid), startingTaskId,
                                mPermissionPolicyInternal));
@@ -3764,19 +3768,7 @@ public class NotificationManagerService extends SystemService {
            try {
                int uid = mPackageManager.getPackageUid(pkg, 0,
                        UserHandle.getUserId(Binder.getCallingUid()));
                List<ActivityManager.AppTask> tasks = mAtm.getAppTasks(pkg, uid);
                for (int i = 0; i < tasks.size(); i++) {
                    ActivityManager.RecentTaskInfo task = tasks.get(i).getTaskInfo();
                    if (mPermissionPolicyInternal == null) {
                        mPermissionPolicyInternal =
                                LocalServices.getService(PermissionPolicyInternal.class);
                    }
                    if (mPermissionPolicyInternal != null
                            && mPermissionPolicyInternal.canShowPermissionPromptForTask(task)) {
                        taskId = task.taskId;
                        break;
                    }
                }
                taskId = mAtm.getTaskToShowPermissionDialogOn(pkg, uid);
            } catch (RemoteException e) {
                // Do nothing
            }
+15 −2
Original line number Diff line number Diff line
@@ -57,6 +57,7 @@ public abstract class PermissionPolicyInternal {
     * prompt should be shown if the app targets S-, is currently running in a visible, focused
     * task, has the REVIEW_REQUIRED flag set on its implicit notification permission, and has
     * created at least one notification channel (even if it has since been deleted).
     *
     * @param packageName The package whose permission is being checked
     * @param userId The user for whom the package is being started
     * @param taskId The task the notification prompt should be attached to
@@ -66,10 +67,22 @@ public abstract class PermissionPolicyInternal {

    /**
     * Determine if a particular task is in the proper state to show a system-triggered permission
     * prompt. A prompt can be shown if the task is focused, visible, and running.
     * prompt. A prompt can be shown if the task is focused, visible, and running and
     * 1. The intent is a launcher intent (action is ACTION_MAIN, category is LAUNCHER), or
     * 2. The activity belongs to the same package as the one which launched the task originally,
     * and the task was started with a launcher intent
     *
     * @param taskInfo The task to be checked
     * @param currPkg The package of the current top visible activity
     * @param intent The intent of the current top visible activity
     */
    public abstract boolean shouldShowNotificationDialogForTask(@Nullable TaskInfo taskInfo,
            @Nullable String currPkg, @Nullable Intent intent);

    /**
     * @return true if an intent will resolve to a permission request dialog activity
     */
    public abstract boolean canShowPermissionPromptForTask(@Nullable TaskInfo taskInfo);
    public abstract boolean isIntentToPermissionDialog(@NonNull Intent intent);

    /**
     * @return Whether the policy is initialized for a user.
+45 −19
Original line number Diff line number Diff line
@@ -21,6 +21,8 @@ import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.MODE_FOREGROUND;
import static android.app.AppOpsManager.MODE_IGNORED;
import static android.app.AppOpsManager.OP_NONE;
import static android.content.pm.PackageManager.ACTION_REQUEST_PERMISSIONS;
import static android.content.pm.PackageManager.ACTION_REQUEST_PERMISSIONS_FOR_OTHER;
import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION;
import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT;
@@ -1016,7 +1018,7 @@ public final class PermissionPolicyService extends SystemService {
                            ActivityInterceptorInfo info) {
                        String action = info.intent.getAction();
                        ActivityInterceptResult result = null;
                        if (!PackageManager.ACTION_REQUEST_PERMISSIONS_FOR_OTHER.equals(action)
                        if (!ACTION_REQUEST_PERMISSIONS_FOR_OTHER.equals(action)
                                && !PackageManager.ACTION_REQUEST_PERMISSIONS.equals(action)) {
                            return null;
                        }
@@ -1033,7 +1035,7 @@ public final class PermissionPolicyService extends SystemService {
                                && !mContinueNotifGrantMessageUids.contains(info.realCallingUid)) {
                            return result;
                        }
                        if (PackageManager.ACTION_REQUEST_PERMISSIONS_FOR_OTHER.equals(action)) {
                        if (ACTION_REQUEST_PERMISSIONS_FOR_OTHER.equals(action)) {
                            String otherPkg = info.intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME);
                            if (otherPkg == null || (mPackageManager.getPermissionFlags(
                                    POST_NOTIFICATIONS, otherPkg, UserHandle.of(info.userId))
@@ -1052,8 +1054,8 @@ public final class PermissionPolicyService extends SystemService {
                    public void onActivityLaunched(TaskInfo taskInfo, ActivityInfo activityInfo,
                            ActivityInterceptorInfo info) {
                        super.onActivityLaunched(taskInfo, activityInfo, info);
                        if (!shouldShowNotificationDialogOrClearFlags(info.intent,
                                info.checkedOptions)) {
                        if (!shouldShowNotificationDialogOrClearFlags(taskInfo,
                                activityInfo.packageName, info.intent, info.checkedOptions, true)) {
                            return;
                        }
                        UserHandle user = UserHandle.of(taskInfo.userId);
@@ -1085,7 +1087,7 @@ public final class PermissionPolicyService extends SystemService {
                return false;
            }

            if (PackageManager.ACTION_REQUEST_PERMISSIONS_FOR_OTHER.equals(intent.getAction())
            if (ACTION_REQUEST_PERMISSIONS_FOR_OTHER.equals(intent.getAction())
                    && (callingUid != Process.SYSTEM_UID || !SYSTEM_PKG.equals(callingPackage))) {
                return false;
            }
@@ -1104,18 +1106,48 @@ public final class PermissionPolicyService extends SystemService {
            launchNotificationPermissionRequestDialog(packageName, user, taskId);
        }

        @Override
        public boolean isIntentToPermissionDialog(@NonNull Intent intent) {
            return Objects.equals(intent.getPackage(),
                    mPackageManager.getPermissionControllerPackageName())
                    && (Objects.equals(intent.getAction(), ACTION_REQUEST_PERMISSIONS_FOR_OTHER)
                    || Objects.equals(intent.getAction(), ACTION_REQUEST_PERMISSIONS));
        }

        @Override
        public boolean shouldShowNotificationDialogForTask(TaskInfo taskInfo, String currPkg,
                Intent intent) {
            return shouldShowNotificationDialogOrClearFlags(
                    taskInfo, currPkg, intent, null, false);
        }

        /**
         * 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:
         * Determine if a particular task is in the proper state to show a system-triggered
         * permission prompt. A prompt can be shown if the task is just starting, or the task is
         * currently focused, visible, and running, and,
         * 1. The isEligibleForLegacyPermissionPrompt ActivityOption is set, or
         * 2. The intent is a launcher intent (action is ACTION_MAIN, category is LAUNCHER)
         * 2. The intent is a launcher intent (action is ACTION_MAIN, category is LAUNCHER), or
         * 3. The activity belongs to the same package as the one which launched the task
         * originally, and the task was started with a launcher intent
         * @param taskInfo The task to be checked
         * @param currPkg The package of the current top visible activity
         * @param intent The intent of the current top visible activity
         */
        private boolean shouldShowNotificationDialogOrClearFlags(Intent intent,
                ActivityOptions options) {
            if ((options != null && options.isEligibleForLegacyPermissionPrompt())) {
                return true;
        private boolean shouldShowNotificationDialogOrClearFlags(TaskInfo taskInfo, String currPkg,
                Intent intent, ActivityOptions options, boolean activityStart) {
            if (intent == null || currPkg == null || taskInfo == null
                    || (!(taskInfo.isFocused && taskInfo.isVisible && taskInfo.isRunning)
                    && !activityStart)) {
                return false;
            }

            return isLauncherIntent(intent)
                    || (options != null && options.isEligibleForLegacyPermissionPrompt())
                    || (currPkg.equals(taskInfo.baseActivity.getPackageName())
                    && isLauncherIntent(taskInfo.baseIntent));
        }

        private boolean isLauncherIntent(Intent intent) {
            return Intent.ACTION_MAIN.equals(intent.getAction())
                    && intent.getCategories() != null
                    && (intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)
@@ -1144,7 +1176,7 @@ public final class PermissionPolicyService extends SystemService {
            Intent grantPermission = mPackageManager
                    .buildRequestPermissionsIntent(new String[] { POST_NOTIFICATIONS });
            grantPermission.setAction(
                    PackageManager.ACTION_REQUEST_PERMISSIONS_FOR_OTHER);
                    ACTION_REQUEST_PERMISSIONS_FOR_OTHER);
            grantPermission.putExtra(Intent.EXTRA_PACKAGE_NAME, pkgName);

            ActivityOptions options = new ActivityOptions(new Bundle());
@@ -1170,12 +1202,6 @@ public final class PermissionPolicyService extends SystemService {
            }
        }

        @Override
        public boolean canShowPermissionPromptForTask(@Nullable TaskInfo taskInfo) {
            return taskInfo != null && taskInfo.isFocused && taskInfo.isVisible
                    && taskInfo.isRunning;
        }

        /**
         * Check if the intent action is removed for the calling package (often based on target SDK
         * version). If the action is removed, we'll silently cancel the activity launch.
+11 −0
Original line number Diff line number Diff line
@@ -680,4 +680,15 @@ public abstract class ActivityTaskManagerInternal {

    /** Get the app tasks for a package */
    public abstract List<ActivityManager.AppTask> getAppTasks(String pkgName, int uid);

    /**
     * Determine if there exists a task which meets the criteria set by the PermissionPolicyService
     * to show a system-owned permission dialog over, for a given package
     * @see PermissionPolicyInternal.shouldShowNotificationDialogForTask
     *
     * @param pkgName The package whose activity must be top
     * @param uid The uid that must have a top activity
     * @return a task ID if a valid task ID is found. Otherwise, return INVALID_TASK_ID
     */
    public abstract int getTaskToShowPermissionDialogOn(String pkgName, int uid);
}
+8 −0
Original line number Diff line number Diff line
@@ -6754,5 +6754,13 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
            }
            return tasks;
        }

        @Override
        public int getTaskToShowPermissionDialogOn(String pkgName, int uid) {
            synchronized (ActivityTaskManagerService.this.mGlobalLock) {
                return ActivityTaskManagerService.this.mRootWindowContainer
                        .getTaskToShowPermissionDialogOn(pkgName, uid);
            }
        }
    }
}
Loading