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

Commit c96575b8 authored by Automerger Merge Worker's avatar Automerger Merge Worker
Browse files

Merge "Statsd-log notification channel changes." into rvc-dev am: 83f3fb46 am: 9eea92e1

Change-Id: I23f0451481c4b51cc931c16451ce5fbeac6f064b
parents 71b6a5fe 9eea92e1
Loading
Loading
Loading
Loading
+213 −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 com.android.server.notification;

import android.annotation.NonNull;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;

import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.util.FrameworkStatsLog;

/**
 * Interface for logging NotificationChannelModified statsd atoms.  Provided as an interface to
 * enable unit-testing - use standard implementation NotificationChannelLoggerImpl in production.
 */
public interface NotificationChannelLogger {
    // The logging interface. Not anticipating a need to override these high-level methods, which by
    // default forward to a lower-level interface.

    /**
     * Log the creation of a notification channel.
     * @param channel The channel.
     * @param uid UID of app that owns the channel.
     * @param pkg Package of app that owns the channel.
     */
    default void logNotificationChannelCreated(@NonNull NotificationChannel channel, int uid,
            String pkg) {
        logNotificationChannel(
                NotificationChannelEvent.getCreated(channel),
                channel, uid, pkg, 0, 0);
    }

    /**
     * Log the deletion of a notification channel.
     * @param channel The channel.
     * @param uid UID of app that owns the channel.
     * @param pkg Package of app that owns the channel.
     */
    default void logNotificationChannelDeleted(@NonNull NotificationChannel channel, int uid,
            String pkg) {
        logNotificationChannel(
                NotificationChannelEvent.getDeleted(channel),
                channel, uid, pkg, 0, 0);
    }

    /**
     * Log the modification of a notification channel.
     * @param channel The channel.
     * @param uid UID of app that owns the channel.
     * @param pkg Package of app that owns the channel.
     * @param oldImportance Previous importance level of the channel.
     * @param byUser True if the modification was user-specified.
     */
    default void logNotificationChannelModified(@NonNull NotificationChannel channel, int uid,
            String pkg, int oldImportance, boolean byUser) {
        logNotificationChannel(NotificationChannelEvent.getUpdated(byUser),
                channel, uid, pkg, oldImportance, channel.getImportance());
    }

    /**
     * Log the creation or modification of a notification channel group.
     * @param channelGroup The notification channel group.
     * @param uid UID of app that owns the channel.
     * @param pkg Package of app that owns the channel.
     * @param isNew True if this is a creation of a new group.
     * @param wasBlocked
     */
    default void logNotificationChannelGroup(@NonNull NotificationChannelGroup channelGroup,
            int uid, String pkg, boolean isNew, boolean wasBlocked) {
        logNotificationChannelGroup(NotificationChannelEvent.getGroupUpdated(isNew),
                channelGroup, uid, pkg, wasBlocked);
    }

    /**
     * Log the deletion of a notification channel group.
     * @param channelGroup The notification channel group.
     * @param uid UID of app that owns the channel.
     * @param pkg Package of app that owns the channel.
     */
    default void logNotificationChannelGroupDeleted(@NonNull NotificationChannelGroup channelGroup,
            int uid, String pkg) {
        logNotificationChannelGroup(NotificationChannelEvent.NOTIFICATION_CHANNEL_GROUP_DELETED,
                channelGroup, uid, pkg, false);
    }

    /**
     * Low-level interface for logging events, to be implemented.
     * @param event Event to log.
     * @param channel Notification channel.
     * @param uid UID of app that owns the channel.
     * @param pkg Package of app that owns the channel.
     * @param oldImportance Old importance of the channel, if applicable (0 otherwise).
     * @param newImportance New importance of the channel, if applicable (0 otherwise).
     */
    void logNotificationChannel(@NonNull NotificationChannelEvent event,
            @NonNull NotificationChannel channel, int uid, String pkg,
            int oldImportance, int newImportance);

    /**
     * Low-level interface for logging channel group events, to be implemented.
     * @param event Event to log.
     * @param channelGroup Notification channel group.
     * @param uid UID of app that owns the channel.
     * @param pkg Package of app that owns the channel.
     * @param wasBlocked True if the channel is being modified and was previously blocked.
     */
    void logNotificationChannelGroup(@NonNull NotificationChannelEvent event,
            @NonNull NotificationChannelGroup channelGroup, int uid, String pkg,
            boolean wasBlocked);

    /**
     * The UiEvent enums that this class can log.
     */
    enum NotificationChannelEvent implements UiEventLogger.UiEventEnum {
        @UiEvent(doc = "App created a new notification channel")
        NOTIFICATION_CHANNEL_CREATED(219),
        @UiEvent(doc = "App modified an existing notification channel")
        NOTIFICATION_CHANNEL_UPDATED(220),
        @UiEvent(doc = "User modified a new notification channel")
        NOTIFICATION_CHANNEL_UPDATED_BY_USER(221),
        @UiEvent(doc = "App deleted an existing notification channel")
        NOTIFICATION_CHANNEL_DELETED(222),
        @UiEvent(doc = "App created a new notification channel group")
        NOTIFICATION_CHANNEL_GROUP_CREATED(223),
        @UiEvent(doc = "App modified an existing notification channel group")
        NOTIFICATION_CHANNEL_GROUP_UPDATED(224),
        @UiEvent(doc = "App deleted an existing notification channel group")
        NOTIFICATION_CHANNEL_GROUP_DELETED(226),
        @UiEvent(doc = "System created a new conversation (sub-channel in a notification channel)")
        NOTIFICATION_CHANNEL_CONVERSATION_CREATED(272),
        @UiEvent(doc = "System deleted a new conversation (sub-channel in a notification channel)")
        NOTIFICATION_CHANNEL_CONVERSATION_DELETED(274);


        private final int mId;
        NotificationChannelEvent(int id) {
            mId = id;
        }
        @Override public int getId() {
            return mId;
        }

        public static NotificationChannelEvent getUpdated(boolean byUser) {
            return byUser
                    ? NotificationChannelEvent.NOTIFICATION_CHANNEL_UPDATED_BY_USER
                    : NotificationChannelEvent.NOTIFICATION_CHANNEL_UPDATED;
        }

        public static NotificationChannelEvent getCreated(@NonNull NotificationChannel channel) {
            return channel.getConversationId() != null
                    ? NotificationChannelEvent.NOTIFICATION_CHANNEL_CONVERSATION_CREATED
                    : NotificationChannelEvent.NOTIFICATION_CHANNEL_CREATED;
        }

        public static NotificationChannelEvent getDeleted(@NonNull NotificationChannel channel) {
            return channel.getConversationId() != null
                    ? NotificationChannelEvent.NOTIFICATION_CHANNEL_CONVERSATION_DELETED
                    : NotificationChannelEvent.NOTIFICATION_CHANNEL_DELETED;
        }

        public static NotificationChannelEvent getGroupUpdated(boolean isNew) {
            return isNew
                    ? NotificationChannelEvent.NOTIFICATION_CHANNEL_GROUP_CREATED
                    : NotificationChannelEvent.NOTIFICATION_CHANNEL_GROUP_DELETED;
        }
    }

    /**
     * @return Small hash of the channel ID, if present, or 0 otherwise.
     */
    static int getIdHash(@NonNull NotificationChannel channel) {
        return NotificationRecordLogger.smallHash(channel.getId());
    }

    /**
     * @return Small hash of the channel ID, if present, or 0 otherwise.
     */
    static int getIdHash(@NonNull NotificationChannelGroup group) {
        return NotificationRecordLogger.smallHash(group.getId());
    }

    /**
     * @return "Importance" for a channel group
     */
    static int getImportance(@NonNull NotificationChannelGroup channelGroup) {
        return getImportance(channelGroup.isBlocked());
    }

    /**
     * @return "Importance" for a channel group, from its blocked status
     */
    static int getImportance(boolean isBlocked) {
        return isBlocked
                ? FrameworkStatsLog.NOTIFICATION_CHANNEL_MODIFIED__IMPORTANCE__IMPORTANCE_NONE
                : FrameworkStatsLog.NOTIFICATION_CHANNEL_MODIFIED__IMPORTANCE__IMPORTANCE_DEFAULT;
    }

}
+54 −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 com.android.server.notification;

import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;

import com.android.internal.util.FrameworkStatsLog;

/**
 * Standard implementation of NotificationChannelLogger, which passes data through to StatsLog.
 * This layer is as skinny as possible, to maximize code coverage of unit tests.  Nontrivial code
 * should live in the interface so it can be tested.
 */
public class NotificationChannelLoggerImpl implements NotificationChannelLogger {
    @Override
    public void logNotificationChannel(NotificationChannelEvent event,
            NotificationChannel channel, int uid, String pkg,
            int oldImportance, int newImportance) {
        FrameworkStatsLog.write(FrameworkStatsLog.NOTIFICATION_CHANNEL_MODIFIED,
                /* int event_id*/ event.getId(),
                /* int uid*/ uid,
                /* String package_name */ pkg,
                /* int32 channel_id_hash */ NotificationChannelLogger.getIdHash(channel),
                /* int old_importance*/ oldImportance,
                /* int importance*/ newImportance);
    }

    @Override
    public void logNotificationChannelGroup(NotificationChannelEvent event,
            NotificationChannelGroup channelGroup, int uid, String pkg, boolean wasBlocked) {
        FrameworkStatsLog.write(FrameworkStatsLog.NOTIFICATION_CHANNEL_MODIFIED,
                /* int event_id*/ event.getId(),
                /* int uid*/ uid,
                /* String package_name */ pkg,
                /* int32 channel_id_hash */ NotificationChannelLogger.getIdHash(channelGroup),
                /* int old_importance*/ NotificationChannelLogger.getImportance(wasBlocked),
                /* int importance*/ NotificationChannelLogger.getImportance(channelGroup));
    }
}
+2 −1
Original line number Diff line number Diff line
@@ -1979,7 +1979,8 @@ public class NotificationManagerService extends SystemService {
        mPreferencesHelper = new PreferencesHelper(getContext(),
                mPackageManagerClient,
                mRankingHandler,
                mZenModeHelper);
                mZenModeHelper,
                new NotificationChannelLoggerImpl());
        mRankingHelper = new RankingHelper(getContext(),
                mRankingHandler,
                mPreferencesHelper,
+22 −14
Original line number Diff line number Diff line
@@ -27,7 +27,6 @@ import android.os.Bundle;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationStats;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;

@@ -307,27 +306,36 @@ public interface NotificationRecordLogger {
         * @return Small hash of the channel ID, if present, or 0 otherwise.
         */
        int getChannelIdHash() {
            return smallHash(Objects.hashCode(r.getSbn().getNotification().getChannelId()));
            return smallHash(r.getSbn().getNotification().getChannelId());
        }

        /**
         * @return Small hash of the group ID, respecting group override if present. 0 otherwise.
         */
        int getGroupIdHash() {
            return smallHash(Objects.hashCode(r.getSbn().getGroup()));
            return smallHash(r.getSbn().getGroup());
        }

    }

    // "Small" hashes will be in the range [0, MAX_HASH).
        static final int MAX_HASH = (1 << 13);
    int MAX_HASH = (1 << 13);

    /**
     * Maps in to the range [0, MAX_HASH), keeping similar values distinct.
     * @param in An arbitrary integer.
     * @return in mod MAX_HASH, signs chosen to stay in the range [0, MAX_HASH).
     */
        @VisibleForTesting
    static int smallHash(int in) {
        return Math.floorMod(in, MAX_HASH);
    }

    /**
     * @return Small hash of the string, if non-null, or 0 otherwise.
     */
    static int smallHash(@Nullable String in) {
        return smallHash(Objects.hashCode(in));
    }


}
+43 −16
Original line number Diff line number Diff line
@@ -145,6 +145,7 @@ public class PreferencesHelper implements RankingConfig {
    private final PackageManager mPm;
    private final RankingHandler mRankingHandler;
    private final ZenModeHelper mZenModeHelper;
    private final NotificationChannelLogger mNotificationChannelLogger;

    private SparseBooleanArray mBadgingEnabled;
    private boolean mBubblesEnabled = DEFAULT_ALLOW_BUBBLE;
@@ -161,11 +162,12 @@ public class PreferencesHelper implements RankingConfig {
    }

    public PreferencesHelper(Context context, PackageManager pm, RankingHandler rankingHandler,
            ZenModeHelper zenHelper) {
            ZenModeHelper zenHelper, NotificationChannelLogger notificationChannelLogger) {
        mContext = context;
        mZenModeHelper = zenHelper;
        mRankingHandler = rankingHandler;
        mPm = pm;
        mNotificationChannelLogger = notificationChannelLogger;

        // STOPSHIP (b/142218092) this should be removed before ship
        if (!wasBadgingForcedTrue(context)) {
@@ -654,10 +656,6 @@ public class PreferencesHelper implements RankingConfig {
                throw new IllegalArgumentException("Invalid package");
            }
            final NotificationChannelGroup oldGroup = r.groups.get(group.getId());
            if (!group.equals(oldGroup)) {
                // will log for new entries as well as name/description changes
                MetricsLogger.action(getChannelGroupLog(group.getId(), pkg));
            }
            if (oldGroup != null) {
                group.setChannels(oldGroup.getChannels());

@@ -674,6 +672,13 @@ public class PreferencesHelper implements RankingConfig {
                    }
                }
            }
            if (!group.equals(oldGroup)) {
                // will log for new entries as well as name/description changes
                MetricsLogger.action(getChannelGroupLog(group.getId(), pkg));
                mNotificationChannelLogger.logNotificationChannelGroup(group, uid, pkg,
                        oldGroup == null,
                        (oldGroup != null) && oldGroup.isBlocked());
            }
            r.groups.put(group.getId(), group);
        }
    }
@@ -685,7 +690,7 @@ public class PreferencesHelper implements RankingConfig {
        Objects.requireNonNull(channel);
        Objects.requireNonNull(channel.getId());
        Preconditions.checkArgument(!TextUtils.isEmpty(channel.getName()));
        boolean needsPolicyFileChange = false;
        boolean needsPolicyFileChange = false, wasUndeleted = false;
        synchronized (mPackagePreferences) {
            PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
            if (r == null) {
@@ -698,15 +703,18 @@ public class PreferencesHelper implements RankingConfig {
                throw new IllegalArgumentException("Reserved id");
            }
            NotificationChannel existing = r.channels.get(channel.getId());
            // Keep most of the existing settings
            if (existing != null && fromTargetApp) {
                // Actually modifying an existing channel - keep most of the existing settings
                if (existing.isDeleted()) {
                    // The existing channel was deleted - undelete it.
                    existing.setDeleted(false);
                    needsPolicyFileChange = true;
                    wasUndeleted = true;

                    // log a resurrected channel as if it's new again
                    MetricsLogger.action(getChannelLog(channel, pkg).setType(
                            com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_OPEN));
                    mNotificationChannelLogger.logNotificationChannelCreated(channel, uid, pkg);
                }

                if (!Objects.equals(channel.getName().toString(), existing.getName().toString())) {
@@ -756,6 +764,10 @@ public class PreferencesHelper implements RankingConfig {
                }

                updateConfig();
                if (needsPolicyFileChange && !wasUndeleted) {
                    mNotificationChannelLogger.logNotificationChannelModified(existing, uid, pkg,
                            previousExistingImportance, false);
                }
                return needsPolicyFileChange;
            }

@@ -806,6 +818,7 @@ public class PreferencesHelper implements RankingConfig {
            }
            MetricsLogger.action(getChannelLog(channel, pkg).setType(
                    com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_OPEN));
            mNotificationChannelLogger.logNotificationChannelCreated(channel, uid, pkg);
        }

        return needsPolicyFileChange;
@@ -867,6 +880,8 @@ public class PreferencesHelper implements RankingConfig {
                // only log if there are real changes
                MetricsLogger.action(getChannelLog(updatedChannel, pkg)
                        .setSubtype(fromUser ? 1 : 0));
                mNotificationChannelLogger.logNotificationChannelModified(updatedChannel, uid, pkg,
                        channel.getImportance(), fromUser);
            }

            if (updatedChannel.canBypassDnd() != mAreChannelsBypassingDnd
@@ -954,17 +969,24 @@ public class PreferencesHelper implements RankingConfig {
            }
            NotificationChannel channel = r.channels.get(channelId);
            if (channel != null) {
                deleteNotificationChannelLocked(channel, pkg, uid);
            }
        }
    }

    private void deleteNotificationChannelLocked(NotificationChannel channel, String pkg, int uid) {
        if (!channel.isDeleted()) {
            channel.setDeleted(true);
            LogMaker lm = getChannelLog(channel, pkg);
            lm.setType(com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_CLOSE);
            MetricsLogger.action(lm);
            mNotificationChannelLogger.logNotificationChannelDeleted(channel, uid, pkg);

            if (mAreChannelsBypassingDnd && channel.canBypassDnd()) {
                updateChannelsBypassingDnd(mContext.getUserId());
            }
        }
    }
    }

    @Override
    @VisibleForTesting
@@ -1158,13 +1180,17 @@ public class PreferencesHelper implements RankingConfig {
                return deletedChannels;
            }

            r.groups.remove(groupId);
            NotificationChannelGroup channelGroup = r.groups.remove(groupId);
            if (channelGroup != null) {
                mNotificationChannelLogger.logNotificationChannelGroupDeleted(channelGroup, uid,
                        pkg);
            }

            int N = r.channels.size();
            for (int i = 0; i < N; i++) {
                final NotificationChannel nc = r.channels.valueAt(i);
                if (groupId.equals(nc.getGroup())) {
                    nc.setDeleted(true);
                    deleteNotificationChannelLocked(nc, pkg, uid);
                    deletedChannels.add(nc);
                }
            }
@@ -1279,6 +1305,7 @@ public class PreferencesHelper implements RankingConfig {
                    lm.setType(
                            com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_CLOSE);
                    MetricsLogger.action(lm);
                    mNotificationChannelLogger.logNotificationChannelDeleted(nc, uid, pkg);

                    deletedChannelIds.add(nc.getId());
                }
Loading