Merge "Proxy notification improvements"
This commit is contained in:
committed by
Android (Google) Code Review
commit
cd80ab8319
@@ -51,7 +51,7 @@
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_toEndOf="@id/pkgicon" />
|
||||
<TextView
|
||||
android:id="@+id/pkg_group_divider"
|
||||
android:id="@+id/pkg_divider"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="@*android:style/TextAppearance.Material.Notification.Info"
|
||||
@@ -61,7 +61,7 @@
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_toEndOf="@id/pkgname" />
|
||||
<TextView
|
||||
android:id="@+id/group_name"
|
||||
android:id="@+id/delegate_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="@*android:style/TextAppearance.Material.Notification.Info"
|
||||
@@ -70,7 +70,7 @@
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_toEndOf="@id/pkg_group_divider" />
|
||||
android:layout_toEndOf="@id/pkg_divider" />
|
||||
<!-- 24 dp icon with 16 dp padding all around to mirror notification content margins -->
|
||||
<ImageButton
|
||||
android:id="@+id/info"
|
||||
@@ -101,13 +101,39 @@
|
||||
android:layout_marginStart="@*android:dimen/notification_content_margin_start"
|
||||
android:layout_marginEnd="@*android:dimen/notification_content_margin_start"
|
||||
android:orientation="vertical">
|
||||
<!-- Channel Name -->
|
||||
<TextView
|
||||
android:id="@+id/channel_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
style="@android:style/TextAppearance.Material.Notification.Title" />
|
||||
<RelativeLayout
|
||||
android:id="@+id/names"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<TextView
|
||||
android:id="@+id/group_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="@*android:style/TextAppearance.Material.Notification.Title"
|
||||
android:layout_marginStart="2dp"
|
||||
android:layout_marginEnd="2dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:layout_centerVertical="true" />
|
||||
<TextView
|
||||
android:id="@+id/pkg_group_divider"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="@*android:style/TextAppearance.Material.Notification.Title"
|
||||
android:layout_marginStart="2dp"
|
||||
android:layout_marginEnd="2dp"
|
||||
android:text="@*android:string/notification_header_divider_symbol"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_toEndOf="@id/group_name" />
|
||||
<!-- Channel Name -->
|
||||
<TextView
|
||||
android:id="@+id/channel_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
style="@android:style/TextAppearance.Material.Notification.Title"
|
||||
android:layout_toEndOf="@id/pkg_group_divider"/>
|
||||
</RelativeLayout>
|
||||
<!-- Question prompt -->
|
||||
<TextView
|
||||
android:id="@+id/block_prompt"
|
||||
|
||||
@@ -1574,6 +1574,9 @@
|
||||
<!-- Notification: Control panel: Label that displays when the app's notifications cannot be blocked. -->
|
||||
<string name="notification_unblockable_desc">These notifications can\'t be turned off</string>
|
||||
|
||||
<!-- Notification: Control panel: Label for the app that posted this notification, if it's not the package that the notification was posted for -->
|
||||
<string name="notification_delegate_header">via <xliff:g id="app_name" example="YouTube">%1$s</xliff:g></string>
|
||||
|
||||
<!-- Notification Inline controls: describes what the app is doing in the background [CHAR_LIMIT=NONE] -->
|
||||
<string name="appops_camera">This app is using the camera.</string>
|
||||
<!-- Notification Inline controls: describes what the app is doing in the background [CHAR_LIMIT=NONE] -->
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user