Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ac98aea2 authored by Julia Reynolds's avatar Julia Reynolds
Browse files

Proxy notification improvments

- Fix a bug where the notifications weren't being posted
- Add attribution to the notification guts

Bug: 111452544
Test: atest
Change-Id: I58d104fe0ad8450a4722585335011ea633fee52a
parent b6bd93d9
Loading
Loading
Loading
Loading
+36 −10
Original line number Original line Diff line number Diff line
@@ -51,7 +51,7 @@
            android:layout_centerVertical="true"
            android:layout_centerVertical="true"
            android:layout_toEndOf="@id/pkgicon" />
            android:layout_toEndOf="@id/pkgicon" />
        <TextView
        <TextView
            android:id="@+id/pkg_group_divider"
            android:id="@+id/pkg_divider"
            android:layout_width="wrap_content"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_height="wrap_content"
            android:textAppearance="@*android:style/TextAppearance.Material.Notification.Info"
            android:textAppearance="@*android:style/TextAppearance.Material.Notification.Info"
@@ -61,7 +61,7 @@
            android:layout_centerVertical="true"
            android:layout_centerVertical="true"
            android:layout_toEndOf="@id/pkgname" />
            android:layout_toEndOf="@id/pkgname" />
        <TextView
        <TextView
            android:id="@+id/group_name"
            android:id="@+id/delegate_name"
            android:layout_width="wrap_content"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_height="wrap_content"
            android:textAppearance="@*android:style/TextAppearance.Material.Notification.Info"
            android:textAppearance="@*android:style/TextAppearance.Material.Notification.Info"
@@ -70,7 +70,7 @@
            android:ellipsize="end"
            android:ellipsize="end"
            android:maxLines="1"
            android:maxLines="1"
            android:layout_centerVertical="true"
            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 -->
        <!-- 24 dp icon with 16 dp padding all around to mirror notification content margins -->
        <ImageButton
        <ImageButton
            android:id="@+id/info"
            android:id="@+id/info"
@@ -101,13 +101,39 @@
            android:layout_marginStart="@*android:dimen/notification_content_margin_start"
            android:layout_marginStart="@*android:dimen/notification_content_margin_start"
            android:layout_marginEnd="@*android:dimen/notification_content_margin_start"
            android:layout_marginEnd="@*android:dimen/notification_content_margin_start"
            android:orientation="vertical">
            android:orientation="vertical">
            <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 -->
                <!-- Channel Name -->
                <TextView
                <TextView
                    android:id="@+id/channel_name"
                    android:id="@+id/channel_name"
                    android:layout_width="wrap_content"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:layout_weight="1"
                style="@android:style/TextAppearance.Material.Notification.Title" />
                    style="@android:style/TextAppearance.Material.Notification.Title"
                    android:layout_toEndOf="@id/pkg_group_divider"/>
            </RelativeLayout>
            <!-- Question prompt -->
            <!-- Question prompt -->
            <TextView
            <TextView
                android:id="@+id/block_prompt"
                android:id="@+id/block_prompt"
+3 −0
Original line number Original line Diff line number Diff line
@@ -1593,6 +1593,9 @@
    <!-- Notification: Control panel: Label that displays when the app's notifications cannot be blocked. -->
    <!-- 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>
    <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] -->
    <!-- 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>
    <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] -->
    <!-- Notification Inline controls: describes what the app is doing in the background [CHAR_LIMIT=NONE] -->
+60 −21
Original line number Original line Diff line number Diff line
@@ -72,6 +72,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
    private String mPackageName;
    private String mPackageName;
    private String mAppName;
    private String mAppName;
    private int mAppUid;
    private int mAppUid;
    private String mDelegatePkg;
    private int mNumUniqueChannelsInRow;
    private int mNumUniqueChannelsInRow;
    private NotificationChannel mSingleNotificationChannel;
    private NotificationChannel mSingleNotificationChannel;
    private int mStartingUserImportance;
    private int mStartingUserImportance;
@@ -193,6 +194,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
                (mSbn.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE) != 0;
                (mSbn.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE) != 0;
        mIsForBlockingHelper = isForBlockingHelper;
        mIsForBlockingHelper = isForBlockingHelper;
        mAppUid = mSbn.getUid();
        mAppUid = mSbn.getUid();
        mDelegatePkg = mSbn.getOpPkg();
        mIsDeviceProvisioned = isDeviceProvisioned;
        mIsDeviceProvisioned = isDeviceProvisioned;


        int numTotalChannels = mINotificationManager.getNumNotificationChannelsForPackage(
        int numTotalChannels = mINotificationManager.getNumNotificationChannelsForPackage(
@@ -234,26 +236,8 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
        ((ImageView) findViewById(R.id.pkgicon)).setImageDrawable(pkgicon);
        ((ImageView) findViewById(R.id.pkgicon)).setImageDrawable(pkgicon);
        ((TextView) findViewById(R.id.pkgname)).setText(mAppName);
        ((TextView) findViewById(R.id.pkgname)).setText(mAppName);


        // Set group information if this channel has an associated group.
        // Delegate
        CharSequence groupName = null;
        bindDelegate();
        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);
        }


        // Settings button.
        // Settings button.
        final View settingsButton = findViewById(R.id.info);
        final View settingsButton = findViewById(R.id.info);
@@ -273,9 +257,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);
        final TextView blockPrompt = findViewById(R.id.block_prompt);
        bindName();
        bindName();
        bindGroup();
        if (mIsNonblockable) {
        if (mIsNonblockable) {
            blockPrompt.setText(R.string.notification_unblockable_desc);
            blockPrompt.setText(R.string.notification_unblockable_desc);
        } else {
        } else {
@@ -298,6 +283,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
    @VisibleForTesting
    void logBlockingHelperCounter(String counterTag) {
    void logBlockingHelperCounter(String counterTag) {
        if (mIsForBlockingHelper) {
        if (mIsForBlockingHelper) {
+30 −1
Original line number Original line Diff line number Diff line
@@ -129,7 +129,7 @@ public class NotificationInfoTest extends SysuiTestCase {
                .thenReturn(packageInfo);
                .thenReturn(packageInfo);
        final ApplicationInfo applicationInfo = new ApplicationInfo();
        final ApplicationInfo applicationInfo = new ApplicationInfo();
        applicationInfo.uid = TEST_UID;  // non-zero
        applicationInfo.uid = TEST_UID;  // non-zero
        when(mMockPackageManager.getApplicationInfo(anyString(), anyInt())).thenReturn(
        when(mMockPackageManager.getApplicationInfo(eq(TEST_PACKAGE_NAME), anyInt())).thenReturn(
                applicationInfo);
                applicationInfo);
        final PackageInfo systemPackageInfo = new PackageInfo();
        final PackageInfo systemPackageInfo = new PackageInfo();
        systemPackageInfo.packageName = TEST_SYSTEM_PACKAGE_NAME;
        systemPackageInfo.packageName = TEST_SYSTEM_PACKAGE_NAME;
@@ -189,6 +189,35 @@ public class NotificationInfoTest extends SysuiTestCase {
        assertEquals(iconDrawable, iconView.getDrawable());
        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);
        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);
        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
    @Test
    public void testBindNotification_GroupNameHiddenIfNoGroup() throws Exception {
    public void testBindNotification_GroupNameHiddenIfNoGroup() throws Exception {
        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+14 −13
Original line number Original line Diff line number Diff line
@@ -4332,19 +4332,20 @@ public class NotificationManagerService extends SystemService {
     *
     *
     * Has side effects.
     * 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) {
            NotificationRecord r, boolean isAutogroup) {
        final String pkg = r.sbn.getPackageName();
        final String pkg = r.sbn.getPackageName();
        final boolean isSystemNotification =
        final boolean isSystemNotification =
                isUidSystemOrPhone(callingUid) || ("android".equals(pkg));
                isUidSystemOrPhone(uid) || ("android".equals(pkg));
        final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg);
        final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg);


        // Limit the number of notifications that any given package except the android
        // 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.
        // package or a registered listener can enqueue.  Prevents DOS attacks and deals with leaks.
        if (!isSystemNotification && !isNotificationFromListener) {
        if (!isSystemNotification && !isNotificationFromListener) {
            synchronized (mNotificationLock) {
            synchronized (mNotificationLock) {
                final int callingUid = Binder.getCallingUid();
                if (mNotificationsByKey.get(r.sbn.getKey()) == null
                if (mNotificationsByKey.get(r.sbn.getKey()) == null
                        && isCallerInstantApp(pkg, Binder.getCallingUid(), userId)) {
                        && isCallerInstantApp(callingUid, userId)) {
                    // Ephemeral apps have some special constraints for notifications.
                    // Ephemeral apps have some special constraints for notifications.
                    // They are not allowed to create new notifications however they are allowed to
                    // They are not allowed to create new notifications however they are allowed to
                    // update notifications created by the system (e.g. a foreground service
                    // update notifications created by the system (e.g. a foreground service
@@ -6452,24 +6453,24 @@ public class NotificationManagerService extends SystemService {
    }
    }


    @VisibleForTesting
    @VisibleForTesting
    boolean isCallerInstantApp(String pkg, int callingUid, int userId) {
    boolean isCallerInstantApp(int callingUid, int userId) {
        // System is always allowed to act for ephemeral apps.
        // System is always allowed to act for ephemeral apps.
        if (isUidSystemOrPhone(callingUid)) {
        if (isUidSystemOrPhone(callingUid)) {
            return false;
            return false;
        }
        }


        try {
            final String pkg = mPackageManager.getNameForUid(callingUid);
            mAppOps.checkPackage(callingUid, pkg);
            mAppOps.checkPackage(callingUid, pkg);


        try {
           ApplicationInfo ai = mPackageManager.getApplicationInfo(pkg, 0, userId);
           ApplicationInfo ai = mPackageManager.getApplicationInfo(pkg, 0, userId);
           if (ai == null) {
           if (ai == null) {
               throw new SecurityException("Unknown package " + pkg);
               throw new SecurityException("Unknown package " + pkg);
           }
           }
           return ai.isInstantApp();
           return ai.isInstantApp();
        } catch (RemoteException re) {
        } catch (RemoteException re) {
            throw new SecurityException("Unknown package " + pkg, re);
            throw new SecurityException("Unknown uid " + callingUid, re);
        }
        }

    }
    }


    private void checkCallerIsSameApp(String pkg) {
    private void checkCallerIsSameApp(String pkg) {
Loading