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

Commit 51582aed authored by Julia Reynolds's avatar Julia Reynolds
Browse files

Add filtering to notifications sent to NLSes

Test: atest
Bug: 173052211
Change-Id: Id1b3440ff375543b546f2a599b791a70e28d8bb8
parent 655f6cdc
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import android.service.notification.Condition;
import android.service.notification.IConditionListener;
import android.service.notification.IConditionProvider;
import android.service.notification.INotificationListener;
import android.service.notification.NotificationListenerFilter;
import android.service.notification.StatusBarNotification;
import android.app.AutomaticZenRule;
import android.service.notification.ZenModeConfig;
@@ -224,4 +225,7 @@ interface INotificationManager
    boolean getPrivateNotificationsAllowed();

    long pullStats(long startNs, int report, boolean doAgg, out List<ParcelFileDescriptor> stats);

    NotificationListenerFilter getListenerFilter(in ComponentName cn, int userId);
    void setListenerFilter(in ComponentName cn, int userId, in NotificationListenerFilter nlf);
}
+20 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2020, The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.service.notification;

parcelable NotificationListenerFilter;
+102 −0
Original line number Diff line number Diff line
/**
 * Copyright (c) 2020, The Android Open Source Project
 *
 * Licensed under the Apache License,  2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package android.service.notification;

import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_SILENT;

import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArraySet;

/**
 * Specifies a filter for what types of notifications should be bridged to notification listeners.
 * Each requested listener will have their own filter instance.
 * @hide
 */
public class NotificationListenerFilter implements Parcelable {
    private int mAllowedNotificationTypes;
    private ArraySet<String> mDisallowedPackages;

    public NotificationListenerFilter() {
        mAllowedNotificationTypes = FLAG_FILTER_TYPE_CONVERSATIONS
                | FLAG_FILTER_TYPE_ALERTING
                | FLAG_FILTER_TYPE_SILENT;
        mDisallowedPackages = new ArraySet<>();
    }

    public NotificationListenerFilter(int types, ArraySet<String> pkgs) {
        mAllowedNotificationTypes = types;
        mDisallowedPackages = pkgs;
    }

    /**
     * @hide
     */
    protected NotificationListenerFilter(Parcel in) {
        mAllowedNotificationTypes = in.readInt();
        mDisallowedPackages = (ArraySet<String>) in.readArraySet(String.class.getClassLoader());
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(mAllowedNotificationTypes);
        dest.writeArraySet(mDisallowedPackages);
    }

    public static final Creator<NotificationListenerFilter> CREATOR =
            new Creator<NotificationListenerFilter>() {
                @Override
                public NotificationListenerFilter createFromParcel(Parcel in) {
                    return new NotificationListenerFilter(in);
                }

                @Override
                public NotificationListenerFilter[] newArray(int size) {
                    return new NotificationListenerFilter[size];
                }
            };

    public boolean isTypeAllowed(int type) {
        return (mAllowedNotificationTypes & type) != 0;
    }

    public boolean isPackageAllowed(String pkg) {
        return !mDisallowedPackages.contains(pkg);
    }

    public int getTypes() {
        return mAllowedNotificationTypes;
    }

    public ArraySet<String> getDisallowedPackages() {
        return mDisallowedPackages;
    }

    public void setTypes(int types) {
        mAllowedNotificationTypes = types;
    }

    public void setDisallowedPackages(ArraySet<String> pkgs) {
        mDisallowedPackages = pkgs;
    }

    @Override
    public int describeContents() {
        return 0;
    }
}
+17 −0
Original line number Diff line number Diff line
@@ -241,6 +241,23 @@ public abstract class NotificationListenerService extends Service {
    })
    public @interface NotificationCancelReason{};

    /**
     * A flag value indicating that this notification listener can see conversation type
     * notifications.
     * @hide
     */
    public static final int FLAG_FILTER_TYPE_CONVERSATIONS = 1;
    /**
     * A flag value indicating that this notification listener can see altering type notifications.
     * @hide
     */
    public static final int FLAG_FILTER_TYPE_ALERTING = 2;
    /**
     * A flag value indicating that this notification listener can see silent type notifications.
     * @hide
     */
    public static final int FLAG_FILTER_TYPE_SILENT = 4;

    /**
     * The full trim of the StatusBarNotification including all its features.
     *
+124 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.service.notification;

import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_SILENT;

import static com.google.common.truth.Truth.assertThat;

import android.app.NotificationChannel;
import android.os.Parcel;
import android.util.ArraySet;

import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;

import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(AndroidJUnit4.class)
@SmallTest
public class NotificationListenerFilterTest {

    @Test
    public void testEmptyConstructor() {
        NotificationListenerFilter nlf = new NotificationListenerFilter();
        assertThat(nlf.isTypeAllowed(FLAG_FILTER_TYPE_CONVERSATIONS)).isTrue();
        assertThat(nlf.isTypeAllowed(FLAG_FILTER_TYPE_ALERTING)).isTrue();
        assertThat(nlf.isTypeAllowed(FLAG_FILTER_TYPE_SILENT)).isTrue();
        assertThat(nlf.getTypes()).isEqualTo(FLAG_FILTER_TYPE_CONVERSATIONS
                | FLAG_FILTER_TYPE_ALERTING
                | FLAG_FILTER_TYPE_SILENT);

        assertThat(nlf.getDisallowedPackages()).isEmpty();
        assertThat(nlf.isPackageAllowed("pkg1")).isTrue();
    }


    @Test
    public void testConstructor() {
        ArraySet<String> pkgs = new ArraySet<>(new String[] {"pkg1", "pkg2"});
        NotificationListenerFilter nlf =
                new NotificationListenerFilter(FLAG_FILTER_TYPE_ALERTING, pkgs);
        assertThat(nlf.isTypeAllowed(FLAG_FILTER_TYPE_CONVERSATIONS)).isFalse();
        assertThat(nlf.isTypeAllowed(FLAG_FILTER_TYPE_ALERTING)).isTrue();
        assertThat(nlf.isTypeAllowed(FLAG_FILTER_TYPE_SILENT)).isFalse();
        assertThat(nlf.getTypes()).isEqualTo(FLAG_FILTER_TYPE_ALERTING);

        assertThat(nlf.getDisallowedPackages()).contains("pkg1");
        assertThat(nlf.getDisallowedPackages()).contains("pkg2");
        assertThat(nlf.isPackageAllowed("pkg1")).isFalse();
        assertThat(nlf.isPackageAllowed("pkg2")).isFalse();
    }

    @Test
    public void testSetDisallowedPackages() {
        NotificationListenerFilter nlf = new NotificationListenerFilter();

        ArraySet<String> pkgs = new ArraySet<>(new String[] {"pkg1"});
        nlf.setDisallowedPackages(pkgs);

        assertThat(nlf.isPackageAllowed("pkg1")).isFalse();
    }

    @Test
    public void testSetTypes() {
        NotificationListenerFilter nlf = new NotificationListenerFilter();

        nlf.setTypes(FLAG_FILTER_TYPE_ALERTING | FLAG_FILTER_TYPE_SILENT);

        assertThat(nlf.isTypeAllowed(FLAG_FILTER_TYPE_CONVERSATIONS)).isFalse();
        assertThat(nlf.isTypeAllowed(FLAG_FILTER_TYPE_ALERTING)).isTrue();
        assertThat(nlf.isTypeAllowed(FLAG_FILTER_TYPE_SILENT)).isTrue();
        assertThat(nlf.getTypes()).isEqualTo(FLAG_FILTER_TYPE_ALERTING
                | FLAG_FILTER_TYPE_SILENT);
    }

    @Test
    public void testDescribeContents() {
        final int expected = 0;
        ArraySet<String> pkgs = new ArraySet<>(new String[] {"pkg1", "pkg2"});
        NotificationListenerFilter nlf =
                new NotificationListenerFilter(FLAG_FILTER_TYPE_ALERTING, pkgs);
        assertThat(nlf.describeContents()).isEqualTo(expected);
    }

    @Test
    public void testParceling() {
        ArraySet<String> pkgs = new ArraySet<>(new String[] {"pkg1", "pkg2"});
        NotificationListenerFilter nlf =
                new NotificationListenerFilter(FLAG_FILTER_TYPE_ALERTING, pkgs);

        Parcel parcel = Parcel.obtain();
        nlf.writeToParcel(parcel, 0);
        parcel.setDataPosition(0);
        NotificationListenerFilter nlf1 =
                NotificationListenerFilter.CREATOR.createFromParcel(parcel);
        assertThat(nlf1.isTypeAllowed(FLAG_FILTER_TYPE_CONVERSATIONS)).isFalse();
        assertThat(nlf1.isTypeAllowed(FLAG_FILTER_TYPE_ALERTING)).isTrue();
        assertThat(nlf1.isTypeAllowed(FLAG_FILTER_TYPE_SILENT)).isFalse();
        assertThat(nlf1.getTypes()).isEqualTo(FLAG_FILTER_TYPE_ALERTING);

        assertThat(nlf1.getDisallowedPackages()).contains("pkg1");
        assertThat(nlf1.getDisallowedPackages()).contains("pkg2");
        assertThat(nlf1.isPackageAllowed("pkg1")).isFalse();
        assertThat(nlf1.isPackageAllowed("pkg2")).isFalse();
    }
}
Loading