diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml
index c86ebe70db5c4..eb3f70adc48b6 100644
--- a/packages/SystemUI/res/layout/notification_info.xml
+++ b/packages/SystemUI/res/layout/notification_info.xml
@@ -51,7 +51,7 @@
android:layout_centerVertical="true"
android:layout_toEndOf="@id/pkgicon" />
+ android:layout_toEndOf="@id/pkg_divider" />
-
-
+
+
+
+
+
+
These notifications can\'t be turned off
+
+ via %1$s
+
This app is using the camera.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index 3a7091bb843ac..0d36d2c2f77ce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -92,6 +92,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
private String mPackageName;
private String mAppName;
private int mAppUid;
+ private String mDelegatePkg;
private int mNumUniqueChannelsInRow;
private NotificationChannel mSingleNotificationChannel;
private int mStartingChannelImportance;
@@ -235,6 +236,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
(mSbn.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE) != 0;
mIsForBlockingHelper = isForBlockingHelper;
mAppUid = mSbn.getUid();
+ mDelegatePkg = mSbn.getOpPkg();
mIsDeviceProvisioned = isDeviceProvisioned;
mIsNoisy = isNoisy;
@@ -281,26 +283,8 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
((ImageView) findViewById(R.id.pkgicon)).setImageDrawable(pkgicon);
((TextView) findViewById(R.id.pkgname)).setText(mAppName);
- // Set group information if this channel has an associated group.
- CharSequence groupName = null;
- if (mSingleNotificationChannel != null && mSingleNotificationChannel.getGroup() != null) {
- final NotificationChannelGroup notificationChannelGroup =
- mINotificationManager.getNotificationChannelGroupForPackage(
- mSingleNotificationChannel.getGroup(), mPackageName, mAppUid);
- if (notificationChannelGroup != null) {
- groupName = notificationChannelGroup.getName();
- }
- }
- TextView groupNameView = findViewById(R.id.group_name);
- TextView groupDividerView = findViewById(R.id.pkg_group_divider);
- if (groupName != null) {
- groupNameView.setText(groupName);
- groupNameView.setVisibility(View.VISIBLE);
- groupDividerView.setVisibility(View.VISIBLE);
- } else {
- groupNameView.setVisibility(View.GONE);
- groupDividerView.setVisibility(View.GONE);
- }
+ // Delegate
+ bindDelegate();
// Settings button.
final View settingsButton = findViewById(R.id.info);
@@ -320,9 +304,10 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
}
}
- private void bindPrompt() {
+ private void bindPrompt() throws RemoteException {
final TextView blockPrompt = findViewById(R.id.block_prompt);
bindName();
+ bindGroup();
if (mIsNonblockable) {
blockPrompt.setText(R.string.notification_unblockable_desc);
} else {
@@ -345,6 +330,60 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
}
}
+ private void bindDelegate() {
+ TextView delegateView = findViewById(R.id.delegate_name);
+ TextView dividerView = findViewById(R.id.pkg_divider);
+
+ CharSequence delegatePkg = null;
+ if (!TextUtils.equals(mPackageName, mDelegatePkg)) {
+ // this notification was posted by a delegate!
+ ApplicationInfo info;
+ try {
+ info = mPm.getApplicationInfo(
+ mDelegatePkg,
+ PackageManager.MATCH_UNINSTALLED_PACKAGES
+ | PackageManager.MATCH_DISABLED_COMPONENTS
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE);
+ if (info != null) {
+ delegatePkg = String.valueOf(mPm.getApplicationLabel(info));
+ }
+ } catch (PackageManager.NameNotFoundException e) { }
+ }
+ if (delegatePkg != null) {
+ delegateView.setText(mContext.getResources().getString(
+ R.string.notification_delegate_header, delegatePkg));
+ delegateView.setVisibility(View.VISIBLE);
+ dividerView.setVisibility(View.VISIBLE);
+ } else {
+ delegateView.setVisibility(View.GONE);
+ dividerView.setVisibility(View.GONE);
+ }
+ }
+
+ private void bindGroup() throws RemoteException {
+ // Set group information if this channel has an associated group.
+ CharSequence groupName = null;
+ if (mSingleNotificationChannel != null && mSingleNotificationChannel.getGroup() != null) {
+ final NotificationChannelGroup notificationChannelGroup =
+ mINotificationManager.getNotificationChannelGroupForPackage(
+ mSingleNotificationChannel.getGroup(), mPackageName, mAppUid);
+ if (notificationChannelGroup != null) {
+ groupName = notificationChannelGroup.getName();
+ }
+ }
+ TextView groupNameView = findViewById(R.id.group_name);
+ TextView groupDividerView = findViewById(R.id.pkg_group_divider);
+ if (groupName != null) {
+ groupNameView.setText(groupName);
+ groupNameView.setVisibility(View.VISIBLE);
+ groupDividerView.setVisibility(View.VISIBLE);
+ } else {
+ groupNameView.setVisibility(View.GONE);
+ groupDividerView.setVisibility(View.GONE);
+ }
+ }
+
@VisibleForTesting
void logBlockingHelperCounter(String counterTag) {
if (mIsForBlockingHelper) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
index 1cc1c637983bb..985827a9cd549 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
@@ -134,7 +134,7 @@ public class NotificationInfoTest extends SysuiTestCase {
.thenReturn(packageInfo);
final ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.uid = TEST_UID; // non-zero
- when(mMockPackageManager.getApplicationInfo(anyString(), anyInt())).thenReturn(
+ when(mMockPackageManager.getApplicationInfo(eq(TEST_PACKAGE_NAME), anyInt())).thenReturn(
applicationInfo);
final PackageInfo systemPackageInfo = new PackageInfo();
systemPackageInfo.packageName = TEST_SYSTEM_PACKAGE_NAME;
@@ -205,6 +205,37 @@ public class NotificationInfoTest extends SysuiTestCase {
assertEquals(iconDrawable, iconView.getDrawable());
}
+ @Test
+ public void testBindNotification_noDelegate() throws Exception {
+ mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
+ assertEquals(GONE, nameView.getVisibility());
+ final TextView dividerView = mNotificationInfo.findViewById(R.id.pkg_divider);
+ assertEquals(GONE, dividerView.getVisibility());
+ }
+
+ @Test
+ public void testBindNotification_delegate() throws Exception {
+ mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, "other", 0, null, TEST_UID, 0,
+ new Notification(), UserHandle.CURRENT, null, 0);
+ final ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.uid = 7; // non-zero
+ when(mMockPackageManager.getApplicationInfo(eq("other"), anyInt())).thenReturn(
+ applicationInfo);
+ when(mMockPackageManager.getApplicationLabel(any())).thenReturn("Other");
+
+ mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
+ assertEquals(VISIBLE, nameView.getVisibility());
+ assertTrue(nameView.getText().toString().contains("Other"));
+ final TextView dividerView = mNotificationInfo.findViewById(R.id.pkg_divider);
+ assertEquals(VISIBLE, dividerView.getVisibility());
+ }
+
@Test
public void testBindNotification_GroupNameHiddenIfNoGroup() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 4da29e4d06790..84577e22c41ff 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -4398,19 +4398,20 @@ public class NotificationManagerService extends SystemService {
*
* Has side effects.
*/
- private boolean checkDisqualifyingFeatures(int userId, int callingUid, int id, String tag,
+ private boolean checkDisqualifyingFeatures(int userId, int uid, int id, String tag,
NotificationRecord r, boolean isAutogroup) {
final String pkg = r.sbn.getPackageName();
final boolean isSystemNotification =
- isUidSystemOrPhone(callingUid) || ("android".equals(pkg));
+ isUidSystemOrPhone(uid) || ("android".equals(pkg));
final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg);
// Limit the number of notifications that any given package except the android
// package or a registered listener can enqueue. Prevents DOS attacks and deals with leaks.
if (!isSystemNotification && !isNotificationFromListener) {
synchronized (mNotificationLock) {
+ final int callingUid = Binder.getCallingUid();
if (mNotificationsByKey.get(r.sbn.getKey()) == null
- && isCallerInstantApp(pkg, Binder.getCallingUid(), userId)) {
+ && isCallerInstantApp(callingUid, userId)) {
// Ephemeral apps have some special constraints for notifications.
// They are not allowed to create new notifications however they are allowed to
// update notifications created by the system (e.g. a foreground service
@@ -6521,24 +6522,28 @@ public class NotificationManagerService extends SystemService {
}
@VisibleForTesting
- boolean isCallerInstantApp(String pkg, int callingUid, int userId) {
+ boolean isCallerInstantApp(int callingUid, int userId) {
// System is always allowed to act for ephemeral apps.
if (isUidSystemOrPhone(callingUid)) {
return false;
}
- mAppOps.checkPackage(callingUid, pkg);
-
try {
+ final String[] pkgs = mPackageManager.getPackagesForUid(callingUid);
+ if (pkgs == null) {
+ throw new SecurityException("Unknown uid " + callingUid);
+ }
+ final String pkg = pkgs[0];
+ mAppOps.checkPackage(callingUid, pkg);
+
ApplicationInfo ai = mPackageManager.getApplicationInfo(pkg, 0, userId);
if (ai == null) {
throw new SecurityException("Unknown package " + pkg);
}
return ai.isInstantApp();
} catch (RemoteException re) {
- throw new SecurityException("Unknown package " + pkg, re);
+ throw new SecurityException("Unknown uid " + callingUid, re);
}
-
}
private void checkCallerIsSameApp(String pkg) {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 3266b8b92a194..5eda14f7f387e 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -68,16 +68,15 @@ import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.app.AppOpsManager;
-import android.app.Application;
import android.app.IActivityManager;
import android.app.INotificationManager;
+import android.app.ITransientNotification;
+import android.app.IUriGrantsManager;
import android.app.Notification;
import android.app.Notification.MessagingStyle.Message;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
-import android.app.ITransientNotification;
-import android.app.IUriGrantsManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.usage.UsageStatsManagerInternal;
import android.companion.ICompanionDeviceManager;
@@ -100,7 +99,6 @@ import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.os.UserManager;
import android.provider.MediaStore;
import android.provider.Settings.Secure;
import android.service.notification.Adjustment;
@@ -116,7 +114,6 @@ import android.testing.TestableLooper.RunWithLooper;
import android.text.Html;
import android.util.ArrayMap;
import android.util.AtomicFile;
-import android.util.Log;
import com.android.internal.R;
import com.android.internal.statusbar.NotificationVisibility;
@@ -288,6 +285,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(false);
when(mUgmInternal.newUriPermissionOwner(anyString())).thenReturn(mPermOwner);
+ when(mPackageManager.getPackagesForUid(mUid)).thenReturn(new String[]{PKG});
// write to a test file; the system file isn't readable from tests
mFile = new File(mContext.getCacheDir(), "test.xml");
@@ -1735,7 +1733,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- public void testGetNotificationChannelFromPrivilegedListener_assistant_noAccess() throws Exception {
+ public void testGetNotificationChannelFromPrivilegedListener_assistant_noAccess()
+ throws Exception {
mService.setPreferencesHelper(mPreferencesHelper);
when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(new ArrayList<>());
when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(false);
@@ -3459,11 +3458,12 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
ApplicationInfo info = new ApplicationInfo();
info.privateFlags = ApplicationInfo.PRIVATE_FLAG_INSTANT;
when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(0))).thenReturn(info);
+ when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{"any"});
- assertTrue(mService.isCallerInstantApp("any", 45770, 0));
+ assertTrue(mService.isCallerInstantApp(45770, 0));
info.privateFlags = 0;
- assertFalse(mService.isCallerInstantApp("any", 575370, 0));
+ assertFalse(mService.isCallerInstantApp(575370, 0));
}
@Test
@@ -3472,8 +3472,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
info.privateFlags = ApplicationInfo.PRIVATE_FLAG_INSTANT;
when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(10))).thenReturn(info);
when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(0))).thenReturn(null);
+ when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{"any"});
- assertTrue(mService.isCallerInstantApp("any", 68638450, 10));
+ assertTrue(mService.isCallerInstantApp(68638450, 10));
}
@Test