Fix notification shell commands

- Notification posting is done as calling app
- make some only callable by shell

Test: atest
Test: sample app; make sure posted notification is from app itself
Test: sample app; make sure some commands fail
Test: make sure all modified commands still work from shell
Fixes: 143339775
Change-Id: I50ee768e792266ad2091f1913168e89d5d1463ed
(cherry picked from commit 1c943a2670c1ff499669b42ef72dcd9f07db08c3)
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 16b33ab..f20957a 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -8638,6 +8638,7 @@
 
     @VisibleForTesting
     void resetAssistantUserSet(int userId) {
+        checkCallerIsSystemOrShell();
         mAssistants.setUserSet(userId, false);
         handleSavePolicyFile();
     }
@@ -8645,12 +8646,14 @@
     @VisibleForTesting
     @Nullable
     ComponentName getApprovedAssistant(int userId) {
+        checkCallerIsSystemOrShell();
         List<ComponentName> allowedComponents = mAssistants.getAllowedComponents(userId);
         return CollectionUtils.firstOrNull(allowedComponents);
     }
 
     @VisibleForTesting
     protected void simulatePackageSuspendBroadcast(boolean suspend, String pkg) {
+        checkCallerIsSystemOrShell();
         // only use for testing: mimic receive broadcast that package is (un)suspended
         // but does not actually (un)suspend the package
         final Bundle extras = new Bundle();
@@ -8667,6 +8670,7 @@
 
     @VisibleForTesting
     protected void simulatePackageDistractionBroadcast(int flag, String[] pkgs) {
+        checkCallerIsSystemOrShell();
         // only use for testing: mimic receive broadcast that package is (un)distracting
         // but does not actually register that info with packagemanager
         final Bundle extras = new Bundle();
diff --git a/services/core/java/com/android/server/notification/NotificationShellCmd.java b/services/core/java/com/android/server/notification/NotificationShellCmd.java
index dd0f420..c6c9896 100644
--- a/services/core/java/com/android/server/notification/NotificationShellCmd.java
+++ b/services/core/java/com/android/server/notification/NotificationShellCmd.java
@@ -16,6 +16,12 @@
 
 package com.android.server.notification;
 
+import static android.app.NotificationManager.INTERRUPTION_FILTER_ALARMS;
+import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL;
+import static android.app.NotificationManager.INTERRUPTION_FILTER_NONE;
+import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
+import static android.app.NotificationManager.INTERRUPTION_FILTER_UNKNOWN;
+
 import android.app.ActivityManager;
 import android.app.INotificationManager;
 import android.app.Notification;
@@ -26,6 +32,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.content.pm.ParceledListSlice;
 import android.content.res.Resources;
 import android.graphics.drawable.BitmapDrawable;
@@ -47,8 +54,8 @@
  * Implementation of `cmd notification` in NotificationManagerService.
  */
 public class NotificationShellCmd extends ShellCommand {
-    private static final String USAGE =
-              "usage: cmd notification SUBCMD [args]\n\n"
+    private static final String TAG = "NotifShellCmd";
+    private static final String USAGE = "usage: cmd notification SUBCMD [args]\n\n"
             + "SUBCMDs:\n"
             + "  allow_listener COMPONENT [user_id (current user if not specified)]\n"
             + "  disallow_listener COMPONENT [user_id (current user if not specified)]\n"
@@ -89,18 +96,19 @@
             + "an <intentspec> is (broadcast|service|activity) <args>\n"
             + "  <args> are as described in `am start`";
 
-    public static final int NOTIFICATION_ID = 1138;
-    public static final String NOTIFICATION_PACKAGE = "com.android.shell";
-    public static final String CHANNEL_ID = "shellcmd";
+    public static final int NOTIFICATION_ID = 2020;
+    public static final String CHANNEL_ID = "shell_cmd";
     public static final String CHANNEL_NAME = "Shell command";
     public static final int CHANNEL_IMP = NotificationManager.IMPORTANCE_DEFAULT;
 
     private final NotificationManagerService mDirectService;
     private final INotificationManager mBinderService;
+    private final PackageManager mPm;
 
     public NotificationShellCmd(NotificationManagerService service) {
         mDirectService = service;
         mBinderService = service.getBinderService();
+        mPm = mDirectService.getContext().getPackageManager();
     }
 
     @Override
@@ -108,9 +116,44 @@
         if (cmd == null) {
             return handleDefaultCommands(cmd);
         }
+        String callingPackage = null;
+        final int callingUid = Binder.getCallingUid();
+        long identity = Binder.clearCallingIdentity();
+        try {
+            String[] packages = mPm.getPackagesForUid(callingUid);
+            if (packages != null && packages.length > 0) {
+                callingPackage = packages[0];
+            }
+        } catch (Exception e) {
+            Slog.e(TAG, "failed to get caller pkg", e);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
         final PrintWriter pw = getOutPrintWriter();
         try {
             switch (cmd.replace('-', '_')) {
+                case "set_dnd": {
+                    String mode = getNextArgRequired();
+                    int interruptionFilter = INTERRUPTION_FILTER_UNKNOWN;
+                    switch(mode) {
+                        case "none":
+                        case "on":
+                            interruptionFilter = INTERRUPTION_FILTER_NONE;
+                            break;
+                        case "priority":
+                            interruptionFilter = INTERRUPTION_FILTER_PRIORITY;
+                            break;
+                        case "alarms":
+                            interruptionFilter = INTERRUPTION_FILTER_ALARMS;
+                            break;
+                        case "all":
+                        case "off":
+                            interruptionFilter = INTERRUPTION_FILTER_ALL;
+                    }
+                    final int filter = interruptionFilter;
+                    mBinderService.setInterruptionFilter(callingPackage, filter);
+                }
+                break;
                 case "allow_dnd": {
                     String packageName = getNextArgRequired();
                     int userId = ActivityManager.getCurrentUser();
@@ -226,7 +269,7 @@
                 }
                 case "post":
                 case "notify":
-                    doNotify(pw);
+                    doNotify(pw, callingPackage, callingUid);
                     break;
                 default:
                     return handleDefaultCommands(cmd);
@@ -238,27 +281,14 @@
         return 0;
     }
 
-    void ensureChannel() throws RemoteException {
-        final int uid = Binder.getCallingUid();
-        final int userid = UserHandle.getCallingUserId();
-        final long token = Binder.clearCallingIdentity();
-        try {
-            if (mBinderService.getNotificationChannelForPackage(NOTIFICATION_PACKAGE,
-                    uid, CHANNEL_ID, false) == null) {
-                final NotificationChannel chan = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME,
-                        CHANNEL_IMP);
-                Slog.v(NotificationManagerService.TAG,
-                        "creating shell channel for user " + userid + " uid " + uid + ": " + chan);
-                mBinderService.createNotificationChannelsForPackage(NOTIFICATION_PACKAGE, uid,
-                        new ParceledListSlice<NotificationChannel>(
-                                Collections.singletonList(chan)));
-                Slog.v(NotificationManagerService.TAG, "created channel: "
-                        + mBinderService.getNotificationChannelForPackage(NOTIFICATION_PACKAGE,
-                                uid, CHANNEL_ID, false));
-            }
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
+    void ensureChannel(String callingPackage, int callingUid) throws RemoteException {
+        final NotificationChannel channel =
+                new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, CHANNEL_IMP);
+        mBinderService.createNotificationChannels(callingPackage,
+                new ParceledListSlice<>(Collections.singletonList(channel)));
+        Slog.v(NotificationManagerService.TAG, "created channel: "
+                + mBinderService.getNotificationChannel(callingPackage,
+                UserHandle.getUserId(callingUid), callingPackage, CHANNEL_ID));
     }
 
     Icon parseIcon(Resources res, String encoded) throws IllegalArgumentException {
@@ -287,7 +317,8 @@
         return null;
     }
 
-    private int doNotify(PrintWriter pw) throws RemoteException, URISyntaxException {
+    private int doNotify(PrintWriter pw, String callingPackage, int callingUid)
+            throws RemoteException, URISyntaxException {
         final Context context = mDirectService.getContext();
         final Resources res = context.getResources();
         final Notification.Builder builder = new Notification.Builder(context, CHANNEL_ID);
@@ -481,26 +512,18 @@
             builder.setSmallIcon(smallIcon);
         }
 
-        ensureChannel();
+        ensureChannel(callingPackage, callingUid);
 
         final Notification n = builder.build();
         pw.println("posting:\n  " + n);
         Slog.v("NotificationManager", "posting: " + n);
 
-        final int userId = UserHandle.getCallingUserId();
-        final long token = Binder.clearCallingIdentity();
-        try {
-            mBinderService.enqueueNotificationWithTag(
-                    NOTIFICATION_PACKAGE, "android",
-                    tag, NOTIFICATION_ID,
-                    n, userId);
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
+        mBinderService.enqueueNotificationWithTag(callingPackage, callingPackage, tag,
+                NOTIFICATION_ID, n, UserHandle.getUserId(callingUid));
 
         if (verbose) {
             NotificationRecord nr = mDirectService.findNotificationLocked(
-                    NOTIFICATION_PACKAGE, tag, NOTIFICATION_ID, userId);
+                    callingPackage, tag, NOTIFICATION_ID, UserHandle.getUserId(callingUid));
             for (int tries = 3; tries-- > 0; ) {
                 if (nr != null) break;
                 try {
@@ -509,7 +532,7 @@
                 } catch (InterruptedException e) {
                 }
                 nr = mDirectService.findNotificationLocked(
-                        NOTIFICATION_PACKAGE, tag, NOTIFICATION_ID, userId);
+                        callingPackage, tag, NOTIFICATION_ID, UserHandle.getUserId(callingUid));
             }
             if (nr == null) {
                 pw.println("warning: couldn't find notification after enqueueing");
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationShellCmdTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationShellCmdTest.java
index fa90b29..0d44318 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationShellCmdTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationShellCmdTest.java
@@ -116,8 +116,8 @@
         ArgumentCaptor<Notification> notificationCaptor =
                 ArgumentCaptor.forClass(Notification.class);
         verify(mMockBinderService).enqueueNotificationWithTag(
-                eq(NotificationShellCmd.NOTIFICATION_PACKAGE),
-                eq("android"),
+                eq(getContext().getPackageName()),
+                eq(getContext().getPackageName()),
                 eq(aTag),
                 eq(NotificationShellCmd.NOTIFICATION_ID),
                 notificationCaptor.capture(),