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