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 1c943a2670)
This commit is contained in:
@@ -8638,6 +8638,7 @@ public class NotificationManagerService extends SystemService {
|
|||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void resetAssistantUserSet(int userId) {
|
void resetAssistantUserSet(int userId) {
|
||||||
|
checkCallerIsSystemOrShell();
|
||||||
mAssistants.setUserSet(userId, false);
|
mAssistants.setUserSet(userId, false);
|
||||||
handleSavePolicyFile();
|
handleSavePolicyFile();
|
||||||
}
|
}
|
||||||
@@ -8645,12 +8646,14 @@ public class NotificationManagerService extends SystemService {
|
|||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
@Nullable
|
@Nullable
|
||||||
ComponentName getApprovedAssistant(int userId) {
|
ComponentName getApprovedAssistant(int userId) {
|
||||||
|
checkCallerIsSystemOrShell();
|
||||||
List<ComponentName> allowedComponents = mAssistants.getAllowedComponents(userId);
|
List<ComponentName> allowedComponents = mAssistants.getAllowedComponents(userId);
|
||||||
return CollectionUtils.firstOrNull(allowedComponents);
|
return CollectionUtils.firstOrNull(allowedComponents);
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
protected void simulatePackageSuspendBroadcast(boolean suspend, String pkg) {
|
protected void simulatePackageSuspendBroadcast(boolean suspend, String pkg) {
|
||||||
|
checkCallerIsSystemOrShell();
|
||||||
// only use for testing: mimic receive broadcast that package is (un)suspended
|
// only use for testing: mimic receive broadcast that package is (un)suspended
|
||||||
// but does not actually (un)suspend the package
|
// but does not actually (un)suspend the package
|
||||||
final Bundle extras = new Bundle();
|
final Bundle extras = new Bundle();
|
||||||
@@ -8667,6 +8670,7 @@ public class NotificationManagerService extends SystemService {
|
|||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
protected void simulatePackageDistractionBroadcast(int flag, String[] pkgs) {
|
protected void simulatePackageDistractionBroadcast(int flag, String[] pkgs) {
|
||||||
|
checkCallerIsSystemOrShell();
|
||||||
// only use for testing: mimic receive broadcast that package is (un)distracting
|
// only use for testing: mimic receive broadcast that package is (un)distracting
|
||||||
// but does not actually register that info with packagemanager
|
// but does not actually register that info with packagemanager
|
||||||
final Bundle extras = new Bundle();
|
final Bundle extras = new Bundle();
|
||||||
|
|||||||
@@ -16,6 +16,12 @@
|
|||||||
|
|
||||||
package com.android.server.notification;
|
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.ActivityManager;
|
||||||
import android.app.INotificationManager;
|
import android.app.INotificationManager;
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
@@ -26,6 +32,7 @@ import android.app.Person;
|
|||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
import android.content.pm.ParceledListSlice;
|
import android.content.pm.ParceledListSlice;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.graphics.drawable.BitmapDrawable;
|
import android.graphics.drawable.BitmapDrawable;
|
||||||
@@ -47,8 +54,8 @@ import java.util.Collections;
|
|||||||
* Implementation of `cmd notification` in NotificationManagerService.
|
* Implementation of `cmd notification` in NotificationManagerService.
|
||||||
*/
|
*/
|
||||||
public class NotificationShellCmd extends ShellCommand {
|
public class NotificationShellCmd extends ShellCommand {
|
||||||
private static final String USAGE =
|
private static final String TAG = "NotifShellCmd";
|
||||||
"usage: cmd notification SUBCMD [args]\n\n"
|
private static final String USAGE = "usage: cmd notification SUBCMD [args]\n\n"
|
||||||
+ "SUBCMDs:\n"
|
+ "SUBCMDs:\n"
|
||||||
+ " allow_listener COMPONENT [user_id (current user if not specified)]\n"
|
+ " allow_listener COMPONENT [user_id (current user if not specified)]\n"
|
||||||
+ " disallow_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 @@ public class NotificationShellCmd extends ShellCommand {
|
|||||||
+ "an <intentspec> is (broadcast|service|activity) <args>\n"
|
+ "an <intentspec> is (broadcast|service|activity) <args>\n"
|
||||||
+ " <args> are as described in `am start`";
|
+ " <args> are as described in `am start`";
|
||||||
|
|
||||||
public static final int NOTIFICATION_ID = 1138;
|
public static final int NOTIFICATION_ID = 2020;
|
||||||
public static final String NOTIFICATION_PACKAGE = "com.android.shell";
|
public static final String CHANNEL_ID = "shell_cmd";
|
||||||
public static final String CHANNEL_ID = "shellcmd";
|
|
||||||
public static final String CHANNEL_NAME = "Shell command";
|
public static final String CHANNEL_NAME = "Shell command";
|
||||||
public static final int CHANNEL_IMP = NotificationManager.IMPORTANCE_DEFAULT;
|
public static final int CHANNEL_IMP = NotificationManager.IMPORTANCE_DEFAULT;
|
||||||
|
|
||||||
private final NotificationManagerService mDirectService;
|
private final NotificationManagerService mDirectService;
|
||||||
private final INotificationManager mBinderService;
|
private final INotificationManager mBinderService;
|
||||||
|
private final PackageManager mPm;
|
||||||
|
|
||||||
public NotificationShellCmd(NotificationManagerService service) {
|
public NotificationShellCmd(NotificationManagerService service) {
|
||||||
mDirectService = service;
|
mDirectService = service;
|
||||||
mBinderService = service.getBinderService();
|
mBinderService = service.getBinderService();
|
||||||
|
mPm = mDirectService.getContext().getPackageManager();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -108,9 +116,44 @@ public class NotificationShellCmd extends ShellCommand {
|
|||||||
if (cmd == null) {
|
if (cmd == null) {
|
||||||
return handleDefaultCommands(cmd);
|
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();
|
final PrintWriter pw = getOutPrintWriter();
|
||||||
try {
|
try {
|
||||||
switch (cmd.replace('-', '_')) {
|
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": {
|
case "allow_dnd": {
|
||||||
String packageName = getNextArgRequired();
|
String packageName = getNextArgRequired();
|
||||||
int userId = ActivityManager.getCurrentUser();
|
int userId = ActivityManager.getCurrentUser();
|
||||||
@@ -226,7 +269,7 @@ public class NotificationShellCmd extends ShellCommand {
|
|||||||
}
|
}
|
||||||
case "post":
|
case "post":
|
||||||
case "notify":
|
case "notify":
|
||||||
doNotify(pw);
|
doNotify(pw, callingPackage, callingUid);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return handleDefaultCommands(cmd);
|
return handleDefaultCommands(cmd);
|
||||||
@@ -238,27 +281,14 @@ public class NotificationShellCmd extends ShellCommand {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ensureChannel() throws RemoteException {
|
void ensureChannel(String callingPackage, int callingUid) throws RemoteException {
|
||||||
final int uid = Binder.getCallingUid();
|
final NotificationChannel channel =
|
||||||
final int userid = UserHandle.getCallingUserId();
|
new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, CHANNEL_IMP);
|
||||||
final long token = Binder.clearCallingIdentity();
|
mBinderService.createNotificationChannels(callingPackage,
|
||||||
try {
|
new ParceledListSlice<>(Collections.singletonList(channel)));
|
||||||
if (mBinderService.getNotificationChannelForPackage(NOTIFICATION_PACKAGE,
|
Slog.v(NotificationManagerService.TAG, "created channel: "
|
||||||
uid, CHANNEL_ID, false) == null) {
|
+ mBinderService.getNotificationChannel(callingPackage,
|
||||||
final NotificationChannel chan = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME,
|
UserHandle.getUserId(callingUid), callingPackage, CHANNEL_ID));
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Icon parseIcon(Resources res, String encoded) throws IllegalArgumentException {
|
Icon parseIcon(Resources res, String encoded) throws IllegalArgumentException {
|
||||||
@@ -287,7 +317,8 @@ public class NotificationShellCmd extends ShellCommand {
|
|||||||
return null;
|
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 Context context = mDirectService.getContext();
|
||||||
final Resources res = context.getResources();
|
final Resources res = context.getResources();
|
||||||
final Notification.Builder builder = new Notification.Builder(context, CHANNEL_ID);
|
final Notification.Builder builder = new Notification.Builder(context, CHANNEL_ID);
|
||||||
@@ -481,26 +512,18 @@ public class NotificationShellCmd extends ShellCommand {
|
|||||||
builder.setSmallIcon(smallIcon);
|
builder.setSmallIcon(smallIcon);
|
||||||
}
|
}
|
||||||
|
|
||||||
ensureChannel();
|
ensureChannel(callingPackage, callingUid);
|
||||||
|
|
||||||
final Notification n = builder.build();
|
final Notification n = builder.build();
|
||||||
pw.println("posting:\n " + n);
|
pw.println("posting:\n " + n);
|
||||||
Slog.v("NotificationManager", "posting: " + n);
|
Slog.v("NotificationManager", "posting: " + n);
|
||||||
|
|
||||||
final int userId = UserHandle.getCallingUserId();
|
mBinderService.enqueueNotificationWithTag(callingPackage, callingPackage, tag,
|
||||||
final long token = Binder.clearCallingIdentity();
|
NOTIFICATION_ID, n, UserHandle.getUserId(callingUid));
|
||||||
try {
|
|
||||||
mBinderService.enqueueNotificationWithTag(
|
|
||||||
NOTIFICATION_PACKAGE, "android",
|
|
||||||
tag, NOTIFICATION_ID,
|
|
||||||
n, userId);
|
|
||||||
} finally {
|
|
||||||
Binder.restoreCallingIdentity(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
NotificationRecord nr = mDirectService.findNotificationLocked(
|
NotificationRecord nr = mDirectService.findNotificationLocked(
|
||||||
NOTIFICATION_PACKAGE, tag, NOTIFICATION_ID, userId);
|
callingPackage, tag, NOTIFICATION_ID, UserHandle.getUserId(callingUid));
|
||||||
for (int tries = 3; tries-- > 0; ) {
|
for (int tries = 3; tries-- > 0; ) {
|
||||||
if (nr != null) break;
|
if (nr != null) break;
|
||||||
try {
|
try {
|
||||||
@@ -509,7 +532,7 @@ public class NotificationShellCmd extends ShellCommand {
|
|||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
}
|
}
|
||||||
nr = mDirectService.findNotificationLocked(
|
nr = mDirectService.findNotificationLocked(
|
||||||
NOTIFICATION_PACKAGE, tag, NOTIFICATION_ID, userId);
|
callingPackage, tag, NOTIFICATION_ID, UserHandle.getUserId(callingUid));
|
||||||
}
|
}
|
||||||
if (nr == null) {
|
if (nr == null) {
|
||||||
pw.println("warning: couldn't find notification after enqueueing");
|
pw.println("warning: couldn't find notification after enqueueing");
|
||||||
|
|||||||
@@ -116,8 +116,8 @@ public class NotificationShellCmdTest extends UiServiceTestCase {
|
|||||||
ArgumentCaptor<Notification> notificationCaptor =
|
ArgumentCaptor<Notification> notificationCaptor =
|
||||||
ArgumentCaptor.forClass(Notification.class);
|
ArgumentCaptor.forClass(Notification.class);
|
||||||
verify(mMockBinderService).enqueueNotificationWithTag(
|
verify(mMockBinderService).enqueueNotificationWithTag(
|
||||||
eq(NotificationShellCmd.NOTIFICATION_PACKAGE),
|
eq(getContext().getPackageName()),
|
||||||
eq("android"),
|
eq(getContext().getPackageName()),
|
||||||
eq(aTag),
|
eq(aTag),
|
||||||
eq(NotificationShellCmd.NOTIFICATION_ID),
|
eq(NotificationShellCmd.NOTIFICATION_ID),
|
||||||
notificationCaptor.capture(),
|
notificationCaptor.capture(),
|
||||||
|
|||||||
Reference in New Issue
Block a user