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

Commit c570c01f authored by Julia Reynolds's avatar Julia Reynolds Committed by android-build-merger
Browse files

Merge "Allow system apps to make channels that bypass DND" into pi-dev

am: a4a76af3

Change-Id: I13f1ec2867d1fac51c76baa21d43ccaa30197f68
parents 154596c1 a4a76af3
Loading
Loading
Loading
Loading
+83 −2
Original line number Diff line number Diff line
@@ -29,18 +29,23 @@ import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ParceledListSlice;
import android.content.pm.Signature;
import android.content.res.Resources;
import android.metrics.LogMaker;
import android.os.Build;
import android.os.UserHandle;
import android.print.PrintManager;
import android.provider.Settings.Secure;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.RankingHelperProto;
import android.service.notification.RankingHelperProto.RecordProto;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseBooleanArray;
import android.util.proto.ProtoOutputStream;
@@ -95,12 +100,18 @@ public class RankingHelper implements RankingConfig {
    private final ArrayMap<String, Record> mRecords = new ArrayMap<>(); // pkg|uid => Record
    private final ArrayMap<String, NotificationRecord> mProxyByGroupTmp = new ArrayMap<>();
    private final ArrayMap<String, Record> mRestoredWithoutUids = new ArrayMap<>(); // pkg => Record
    private final ArrayMap<Pair<String, Integer>, Boolean> mSystemAppCache = new ArrayMap<>();

    private final Context mContext;
    private final RankingHandler mRankingHandler;
    private final PackageManager mPm;
    private SparseBooleanArray mBadgingEnabled;

    private Signature[] mSystemSignature;
    private String mPermissionControllerPackageName;
    private String mServicesSystemSharedLibPackageName;
    private String mSharedSystemSharedLibPackageName;

    public RankingHelper(Context context, PackageManager pm, RankingHandler rankingHandler,
            ZenModeHelper zenHelper, NotificationUsageStats usageStats, String[] extractorNames) {
        mContext = context;
@@ -130,6 +141,8 @@ public class RankingHelper implements RankingConfig {
                Slog.w(TAG, "Problem accessing extractor " + extractorNames[i] + ".", e);
            }
        }

        getSignatures();
    }

    @SuppressWarnings("unchecked")
@@ -571,7 +584,7 @@ public class RankingHelper implements RankingConfig {
        if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(channel.getId())) {
            throw new IllegalArgumentException("Reserved id");
        }

        final boolean isSystemApp = isSystemPackage(pkg, uid);
        NotificationChannel existing = r.channels.get(channel.getId());
        // Keep most of the existing settings
        if (existing != null && fromTargetApp) {
@@ -597,6 +610,11 @@ public class RankingHelper implements RankingConfig {
                existing.setImportance(channel.getImportance());
            }

            // system apps can bypass dnd if the user hasn't changed any fields on the channel yet
            if (existing.getUserLockedFields() == 0 & isSystemApp) {
                existing.setBypassDnd(channel.canBypassDnd());
            }

            updateConfig();
            return;
        }
@@ -604,9 +622,12 @@ public class RankingHelper implements RankingConfig {
                || channel.getImportance() > NotificationManager.IMPORTANCE_MAX) {
            throw new IllegalArgumentException("Invalid importance level");
        }

        // Reset fields that apps aren't allowed to set.
        if (fromTargetApp) {
        if (fromTargetApp && !isSystemApp) {
            channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX);
        }
        if (fromTargetApp) {
            channel.setLockscreenVisibility(r.visibility);
        }
        clearLockedFields(channel);
@@ -616,6 +637,7 @@ public class RankingHelper implements RankingConfig {
        if (!r.showBadge) {
            channel.setShowBadge(false);
        }

        r.channels.put(channel.getId(), channel);
        MetricsLogger.action(getChannelLog(channel, pkg).setType(
                MetricsProto.MetricsEvent.TYPE_OPEN));
@@ -625,6 +647,65 @@ public class RankingHelper implements RankingConfig {
        channel.unlockFields(channel.getUserLockedFields());
    }

    /**
     * Determine whether a package is a "system package", in which case certain things (like
     * bypassing DND) should be allowed.
     */
    private boolean isSystemPackage(String pkg, int uid) {
        Pair<String, Integer> app = new Pair(pkg, uid);
        if (mSystemAppCache.containsKey(app)) {
            return mSystemAppCache.get(app);
        }

        PackageInfo pi;
        try {
            pi = mPm.getPackageInfoAsUser(
                    pkg, PackageManager.GET_SIGNATURES, UserHandle.getUserId(uid));
        } catch (NameNotFoundException e) {
            Slog.w(TAG, "Can't find pkg", e);
            return false;
        }
        boolean isSystem = (mSystemSignature[0] != null
                && mSystemSignature[0].equals(getFirstSignature(pi)))
                || pkg.equals(mPermissionControllerPackageName)
                || pkg.equals(mServicesSystemSharedLibPackageName)
                || pkg.equals(mSharedSystemSharedLibPackageName)
                || pkg.equals(PrintManager.PRINT_SPOOLER_PACKAGE_NAME)
                || isDeviceProvisioningPackage(pkg);
        mSystemAppCache.put(app, isSystem);
        return isSystem;
    }

    private Signature getFirstSignature(PackageInfo pkg) {
        if (pkg != null && pkg.signatures != null && pkg.signatures.length > 0) {
            return pkg.signatures[0];
        }
        return null;
    }

    private Signature getSystemSignature() {
        try {
            final PackageInfo sys = mPm.getPackageInfoAsUser(
                    "android", PackageManager.GET_SIGNATURES, UserHandle.USER_SYSTEM);
            return getFirstSignature(sys);
        } catch (NameNotFoundException e) {
        }
        return null;
    }

    private boolean isDeviceProvisioningPackage(String packageName) {
        String deviceProvisioningPackage = mContext.getResources().getString(
                com.android.internal.R.string.config_deviceProvisioningPackage);
        return deviceProvisioningPackage != null && deviceProvisioningPackage.equals(packageName);
    }

    private void getSignatures() {
        mSystemSignature = new Signature[]{getSystemSignature()};
        mPermissionControllerPackageName = mPm.getPermissionControllerPackageName();
        mServicesSystemSharedLibPackageName = mPm.getServicesSystemSharedLibraryPackageName();
        mSharedSystemSharedLibPackageName = mPm.getSharedSystemSharedLibraryPackageName();
    }

    @Override
    public void updateNotificationChannel(String pkg, int uid, NotificationChannel updatedChannel,
            boolean fromUser) {
+59 −0
Original line number Diff line number Diff line
@@ -47,7 +47,9 @@ import android.content.ContentProvider;
import android.content.Context;
import android.content.IContentProvider;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.content.res.Resources;
import android.graphics.Color;
import android.media.AudioAttributes;
@@ -97,6 +99,8 @@ public class RankingHelperTest extends UiServiceTestCase {
    private static final UserHandle USER = UserHandle.of(0);
    private static final String UPDATED_PKG = "updatedPkg";
    private static final int UID2 = 1111;
    private static final String SYSTEM_PKG = "android";
    private static final int SYSTEM_UID= 1000;
    private static final UserHandle USER2 = UserHandle.of(10);
    private static final String TEST_CHANNEL_ID = "test_channel_id";
    private static final String TEST_AUTHORITY = "test";
@@ -136,8 +140,15 @@ public class RankingHelperTest extends UiServiceTestCase {
        upgrade.targetSdkVersion = Build.VERSION_CODES.O;
        when(mPm.getApplicationInfoAsUser(eq(PKG), anyInt(), anyInt())).thenReturn(legacy);
        when(mPm.getApplicationInfoAsUser(eq(UPDATED_PKG), anyInt(), anyInt())).thenReturn(upgrade);
        when(mPm.getApplicationInfoAsUser(eq(SYSTEM_PKG), anyInt(), anyInt())).thenReturn(upgrade);
        when(mPm.getPackageUidAsUser(eq(PKG), anyInt())).thenReturn(UID);
        when(mPm.getPackageUidAsUser(eq(UPDATED_PKG), anyInt())).thenReturn(UID2);
        when(mPm.getPackageUidAsUser(eq(SYSTEM_PKG), anyInt())).thenReturn(SYSTEM_UID);
        PackageInfo info = mock(PackageInfo.class);
        info.signatures = new Signature[] {mock(Signature.class)};
        when(mPm.getPackageInfoAsUser(eq(SYSTEM_PKG), anyInt(), anyInt())).thenReturn(info);
        when(mPm.getPackageInfoAsUser(eq(PKG), anyInt(), anyInt()))
                .thenReturn(mock(PackageInfo.class));
        when(mContext.getResources()).thenReturn(
                InstrumentationRegistry.getContext().getResources());
        when(mContext.getContentResolver()).thenReturn(
@@ -1627,4 +1638,52 @@ public class RankingHelperTest extends UiServiceTestCase {
        assertEquals(1, retrieved.getChannels().size());
        compareChannels(a, findChannel(retrieved.getChannels(), a.getId()));
    }

    @Test
    public void testAndroidPkgCanBypassDnd_creation() {

        NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
        test.setBypassDnd(true);

        mHelper.createNotificationChannel(SYSTEM_PKG, SYSTEM_UID, test, true);

        assertTrue(mHelper.getNotificationChannel(SYSTEM_PKG, SYSTEM_UID, "A", false)
                .canBypassDnd());
    }

    @Test
    public void testNormalPkgCannotBypassDnd_creation() {
        NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
        test.setBypassDnd(true);

        mHelper.createNotificationChannel(PKG, 1000, test, true);

        assertFalse(mHelper.getNotificationChannel(PKG, 1000, "A", false).canBypassDnd());
    }

    @Test
    public void testAndroidPkgCanBypassDnd_update() throws Exception {
        NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
        mHelper.createNotificationChannel(SYSTEM_PKG, SYSTEM_UID, test, true);

        NotificationChannel update = new NotificationChannel("A", "a", IMPORTANCE_LOW);
        update.setBypassDnd(true);
        mHelper.createNotificationChannel(SYSTEM_PKG, SYSTEM_UID, update, true);

        assertTrue(mHelper.getNotificationChannel(SYSTEM_PKG, SYSTEM_UID, "A", false)
                .canBypassDnd());

        // setup + 1st check
        verify(mPm, times(2)).getPackageInfoAsUser(any(), anyInt(), anyInt());
    }

    @Test
    public void testNormalPkgCannotBypassDnd_update() {
        NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
        mHelper.createNotificationChannel(PKG, 1000, test, true);
        NotificationChannel update = new NotificationChannel("A", "a", IMPORTANCE_LOW);
        update.setBypassDnd(true);
        mHelper.createNotificationChannel(PKG, 1000, update, true);
        assertFalse(mHelper.getNotificationChannel(PKG, 1000, "A", false).canBypassDnd());
    }
}