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

Commit ebc2f29d authored by Nate Myren's avatar Nate Myren
Browse files

Intercept activity start to check for notification permissions

The dialog is shown if the app is not a signature app, the system is
ready, and the app has created at least one notification channel.
Also applies the SHOULD_SHOW_REQUEST flag to all packages with implicit
POST_NOTIFICATIONS permissions

Bug: 194833441
Test: atest NotificationPermissionTest
Change-Id: I4cb8cc7bcc3635f55e291f176f722dd420a4a1bb
parent 9710d3a2
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -3016,6 +3016,7 @@ package android.content.pm {
    method @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS}) public abstract void updatePermissionFlags(@NonNull String, @NonNull String, @android.content.pm.PackageManager.PermissionFlags int, @android.content.pm.PackageManager.PermissionFlags int, @NonNull android.os.UserHandle);
    method @Deprecated @RequiresPermission(android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT) public abstract void verifyIntentFilter(int, int, @NonNull java.util.List<java.lang.String>);
    field public static final String ACTION_REQUEST_PERMISSIONS = "android.content.pm.action.REQUEST_PERMISSIONS";
    field public static final String ACTION_REQUEST_PERMISSIONS_FOR_OTHER = "android.content.pm.action.REQUEST_PERMISSIONS_FOR_OTHER";
    field public static final String EXTRA_REQUEST_PERMISSIONS_NAMES = "android.content.pm.extra.REQUEST_PERMISSIONS_NAMES";
    field public static final String EXTRA_REQUEST_PERMISSIONS_RESULTS = "android.content.pm.extra.REQUEST_PERMISSIONS_RESULTS";
    field public static final String FEATURE_BROADCAST_RADIO = "android.hardware.broadcastradio";
+14 −2
Original line number Diff line number Diff line
@@ -4214,6 +4214,17 @@ public abstract class PackageManager {
    public static final String ACTION_REQUEST_PERMISSIONS =
            "android.content.pm.action.REQUEST_PERMISSIONS";

    /**
     * The action used to request that the user approve a permission request
     * from the application. Sent from an application other than the one whose permissions
     * will be granted. Can only be used by the system server.
     *
     * @hide
     */
    @SystemApi
    public static final String ACTION_REQUEST_PERMISSIONS_FOR_OTHER =
            "android.content.pm.action.REQUEST_PERMISSIONS_FOR_OTHER";

    /**
     * The names of the requested permissions.
     * <p>
@@ -4322,8 +4333,9 @@ public abstract class PackageManager {
    public static final int FLAG_PERMISSION_GRANTED_BY_DEFAULT =  1 << 5;

    /**
     * Permission flag: The permission has to be reviewed before any of
     * the app components can run.
     * Permission flag: If app targetSDK < M, then the permission has to be reviewed before any of
     * the app components can run. If app targetSDK >= M, then the system might need to show a
     * request dialog for this permission on behalf of an app.
     *
     * @hide
     */
+1 −0
Original line number Diff line number Diff line
@@ -9890,6 +9890,7 @@ public final class Settings {
         *
         * @hide
         */
        @Readable
        public static final String NOTIFICATION_PERMISSION_ENABLED =
                "notification_permission_enabled";
+3 −0
Original line number Diff line number Diff line
@@ -36,4 +36,7 @@ public interface NotificationManagerInternal {
    void removeForegroundServiceFlagFromNotification(String pkg, int notificationId, int userId);

    void onConversationRemoved(String pkg, int uid, Set<String> shortcuts);

    /** Get the number of notification channels for a given package */
    int getNumNotificationChannelsForPackage(String pkg, int uid, boolean includeDeleted);
}
+101 −5
Original line number Diff line number Diff line
@@ -133,6 +133,7 @@ import android.annotation.WorkerThread;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.ActivityManagerInternal.ServiceNotificationPolicy;
import android.app.ActivityTaskManager;
import android.app.AlarmManager;
import android.app.AppGlobals;
import android.app.AppOpsManager;
@@ -296,6 +297,7 @@ import com.android.server.notification.toast.TextToastRecord;
import com.android.server.notification.toast.ToastRecord;
import com.android.server.pm.PackageManagerService;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.policy.PermissionPolicyInternal;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.uri.UriGrantsManagerInternal;
import com.android.server.utils.quota.MultiRateLimiter;
@@ -480,6 +482,7 @@ public class NotificationManagerService extends SystemService {
    private IPackageManager mPackageManager;
    private PackageManager mPackageManagerClient;
    private PackageManagerInternal mPackageManagerInternal;
    private PermissionPolicyInternal mPermissionPolicyInternal;
    AudioManager mAudioManager;
    AudioManagerInternal mAudioManagerInternal;
    // Can be null for wear
@@ -2104,6 +2107,7 @@ public class NotificationManagerService extends SystemService {
        mPackageManager = packageManager;
        mPackageManagerClient = packageManagerClient;
        mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
        mPermissionPolicyInternal = LocalServices.getService(PermissionPolicyInternal.class);
        mAppOps = appOps;
        mAppOpsService = iAppOps;
        try {
@@ -3663,9 +3667,19 @@ public class NotificationManagerService extends SystemService {

        private void createNotificationChannelsImpl(String pkg, int uid,
                ParceledListSlice channelsList) {
            createNotificationChannelsImpl(pkg, uid, channelsList,
                    ActivityTaskManager.INVALID_TASK_ID);
        }

        private void createNotificationChannelsImpl(String pkg, int uid,
                ParceledListSlice channelsList, int startingTaskId) {
            List<NotificationChannel> channels = channelsList.getList();
            final int channelsSize = channels.size();
            ParceledListSlice<NotificationChannel> oldChannels =
                    mPreferencesHelper.getNotificationChannels(pkg, uid, true);
            final boolean hadChannel = oldChannels != null && !oldChannels.getList().isEmpty();
            boolean needsPolicyFileChange = false;
            boolean hasRequestedNotificationPermission = false;
            for (int i = 0; i < channelsSize; i++) {
                final NotificationChannel channel = channels.get(i);
                Objects.requireNonNull(channel, "channel in list is null");
@@ -3679,6 +3693,19 @@ public class NotificationManagerService extends SystemService {
                            mPreferencesHelper.getNotificationChannel(pkg, uid, channel.getId(),
                                    false),
                            NOTIFICATION_CHANNEL_OR_GROUP_ADDED);
                    boolean hasChannel = hadChannel || hasRequestedNotificationPermission;
                    if (!hasChannel) {
                        ParceledListSlice<NotificationChannel> currChannels =
                                mPreferencesHelper.getNotificationChannels(pkg, uid, true);
                        hasChannel = currChannels != null && !currChannels.getList().isEmpty();
                    }
                    if (!hadChannel && hasChannel && !hasRequestedNotificationPermission
                            && startingTaskId != ActivityTaskManager.INVALID_TASK_ID) {
                        hasRequestedNotificationPermission = true;
                        mHandler.post(new ShowNotificationPermissionPromptRunnable(pkg,
                                UserHandle.getUserId(uid), startingTaskId,
                                mPermissionPolicyInternal));
                    }
                }
            }
            if (needsPolicyFileChange) {
@@ -3687,10 +3714,29 @@ public class NotificationManagerService extends SystemService {
        }

        @Override
        public void createNotificationChannels(String pkg,
                ParceledListSlice channelsList) {
        public void createNotificationChannels(String pkg, ParceledListSlice channelsList) {
            checkCallerIsSystemOrSameApp(pkg);
            createNotificationChannelsImpl(pkg, Binder.getCallingUid(), channelsList);
            int taskId = ActivityTaskManager.INVALID_TASK_ID;
            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;
                    }
                }
            } catch (RemoteException e) {
                // Do nothing
            }
            createNotificationChannelsImpl(pkg, Binder.getCallingUid(), channelsList, taskId);
        }

        @Override
@@ -3873,8 +3919,8 @@ public class NotificationManagerService extends SystemService {
        public int getNumNotificationChannelsForPackage(String pkg, int uid,
                boolean includeDeleted) {
            enforceSystemOrSystemUI("getNumNotificationChannelsForPackage");
            return mPreferencesHelper.getNotificationChannels(pkg, uid, includeDeleted)
                    .getList().size();
            return NotificationManagerService.this
                    .getNumNotificationChannelsForPackage(pkg, uid, includeDeleted);
        }

        @Override
@@ -6139,8 +6185,20 @@ public class NotificationManagerService extends SystemService {
            sbn.getNotification().flags =
                    (r.mOriginalFlags & ~FLAG_FOREGROUND_SERVICE);
        }

        @Override
        public int getNumNotificationChannelsForPackage(String pkg, int uid,
                boolean includeDeleted) {
            return NotificationManagerService.this
                    .getNumNotificationChannelsForPackage(pkg, uid, includeDeleted);
        }
    };

    int getNumNotificationChannelsForPackage(String pkg, int uid, boolean includeDeleted) {
        return mPreferencesHelper.getNotificationChannels(pkg, uid, includeDeleted).getList()
                .size();
    }

    void cancelNotificationInternal(String pkg, String opPkg, int callingUid, int callingPid,
            String tag, int id, int userId) {
        userId = ActivityManager.handleIncomingUser(callingPid,
@@ -6957,6 +7015,44 @@ public class NotificationManagerService extends SystemService {
        }
    }

    protected static class ShowNotificationPermissionPromptRunnable implements Runnable {
        private final String mPkgName;
        private final int mUserId;
        private final int mTaskId;
        private final PermissionPolicyInternal mPpi;

        ShowNotificationPermissionPromptRunnable(String pkg, int user, int task,
                PermissionPolicyInternal pPi) {
            mPkgName = pkg;
            mUserId = user;
            mTaskId = task;
            mPpi = pPi;
        }

        @Override
        public boolean equals(Object o) {
            if (!(o instanceof ShowNotificationPermissionPromptRunnable)) {
                return false;
            }

            ShowNotificationPermissionPromptRunnable other =
                    (ShowNotificationPermissionPromptRunnable) o;

            return Objects.equals(mPkgName, other.mPkgName) && mUserId == other.mUserId
                    && mTaskId == other.mTaskId;
        }

        @Override
        public int hashCode() {
            return Objects.hash(mPkgName, mUserId, mTaskId);
        }

        @Override
        public void run() {
            mPpi.showNotificationPromptIfNeeded(mPkgName, mUserId, mTaskId);
        }
    }

    protected class EnqueueNotificationRunnable implements Runnable {
        private final NotificationRecord r;
        private final int userId;
Loading