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

Commit 1ce3e3fa authored by Sudheer Shanka's avatar Sudheer Shanka
Browse files

Add some recent broadcast and notification events to the dumpsys.

Bug: 197654954
Test: manual
Change-Id: I5a67894c38253711d1eac98ee7bdb32545f5908c
parent 98e13945
Loading
Loading
Loading
Loading
+221 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.usage;

import static android.app.ActivityManager.procStateToString;

import static com.android.server.usage.BroadcastResponseStatsTracker.NOTIFICATION_EVENT_TYPE_CANCELLED;
import static com.android.server.usage.BroadcastResponseStatsTracker.NOTIFICATION_EVENT_TYPE_POSTED;
import static com.android.server.usage.BroadcastResponseStatsTracker.NOTIFICATION_EVENT_TYPE_UPDATED;
import static com.android.server.usage.BroadcastResponseStatsTracker.TAG;
import static com.android.server.usage.UsageStatsService.DEBUG_RESPONSE_STATS;

import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManager.ProcessState;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Slog;
import android.util.TimeUtils;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.RingBuffer;
import com.android.server.usage.BroadcastResponseStatsTracker.NotificationEventType;

public class BroadcastResponseStatsLogger {

    private static final int MAX_LOG_SIZE =
            ActivityManager.isLowRamDeviceStatic() ? 20 : 50;

    private final Object mLock = new Object();

    @GuardedBy("mLock")
    private final LogBuffer mBroadcastEventsBuffer = new LogBuffer(
            BroadcastEvent.class, MAX_LOG_SIZE);
    @GuardedBy("mLock")
    private final LogBuffer mNotificationEventsBuffer = new LogBuffer(
            NotificationEvent.class, MAX_LOG_SIZE);

    void logBroadcastDispatchEvent(int sourceUid, @NonNull String targetPackage,
            UserHandle targetUser, long idForResponseEvent,
            @ElapsedRealtimeLong long timeStampMs, @ProcessState int targetUidProcessState) {
        synchronized (mLock) {
            if (DEBUG_RESPONSE_STATS) {
                Slog.d(TAG, getBroadcastDispatchEventLog(sourceUid, targetPackage,
                        targetUser.getIdentifier(), idForResponseEvent, timeStampMs,
                        targetUidProcessState));
            }
            mBroadcastEventsBuffer.logBroadcastDispatchEvent(sourceUid, targetPackage,
                    targetUser, idForResponseEvent, timeStampMs, targetUidProcessState);
        }
    }

    void logNotificationEvent(@NotificationEventType int event,
            @NonNull String packageName, UserHandle user, @ElapsedRealtimeLong long timestampMs) {
        synchronized (mLock) {
            if (DEBUG_RESPONSE_STATS) {
                Slog.d(TAG, getNotificationEventLog(event, packageName, user.getIdentifier(),
                        timestampMs));
            }
            mNotificationEventsBuffer.logNotificationEvent(event, packageName, user, timestampMs);
        }
    }

    void dumpLogs(IndentingPrintWriter ipw) {
        synchronized (mLock) {
            ipw.println("Broadcast events (most recent first):");
            ipw.increaseIndent();
            mBroadcastEventsBuffer.reverseDump(ipw);
            ipw.decreaseIndent();

            ipw.println();
            ipw.println("Notification events (most recent first):");
            ipw.increaseIndent();
            mNotificationEventsBuffer.reverseDump(ipw);
            ipw.decreaseIndent();
        }
    }

    private static final class LogBuffer<T extends Data> extends RingBuffer<T> {

        LogBuffer(Class<T> classType, int capacity) {
            super(classType, capacity);
        }

        void logBroadcastDispatchEvent(int sourceUid, @NonNull String targetPackage,
                UserHandle targetUser, long idForResponseEvent,
                @ElapsedRealtimeLong long timeStampMs, @ProcessState int targetUidProcessState) {
            final Data data = getNextSlot();
            if (data == null) return;

            data.reset();
            final BroadcastEvent event = (BroadcastEvent) data;
            event.sourceUid = sourceUid;
            event.targetUserId = targetUser.getIdentifier();
            event.targetUidProcessState = targetUidProcessState;
            event.targetPackage = targetPackage;
            event.idForResponseEvent = idForResponseEvent;
            event.timestampMs = timeStampMs;
        }

        void logNotificationEvent(@NotificationEventType int type,
                @NonNull String packageName, UserHandle user,
                @ElapsedRealtimeLong long timestampMs) {
            final Data data = getNextSlot();
            if (data == null) return;

            data.reset();
            final NotificationEvent event = (NotificationEvent) data;
            event.type = type;
            event.packageName = packageName;
            event.userId = user.getIdentifier();
            event.timestampMs = timestampMs;
        }

        public void reverseDump(IndentingPrintWriter pw) {
            final Data[] allData = toArray();
            for (int i = allData.length - 1; i >= 0; --i) {
                if (allData[i] == null) {
                    continue;
                }
                pw.println(getContent(allData[i]));
            }
        }

        @NonNull
        public String getContent(Data data) {
            return data.toString();
        }
    }

    @NonNull
    private static String getBroadcastDispatchEventLog(int sourceUid, @NonNull String targetPackage,
            @UserIdInt int targetUserId, long idForResponseEvent,
            @ElapsedRealtimeLong long timestampMs, @ProcessState int targetUidProcState) {
        return TextUtils.formatSimple(
                "broadcast:%s; srcUid=%d, tgtPkg=%s, tgtUsr=%d, id=%d, state=%s",
                TimeUtils.formatDuration(timestampMs), sourceUid, targetPackage, targetUserId,
                idForResponseEvent, procStateToString(targetUidProcState));
    }

    @NonNull
    private static String getNotificationEventLog(@NotificationEventType int event,
            @NonNull String packageName, @UserIdInt int userId,
            @ElapsedRealtimeLong long timestampMs) {
        return TextUtils.formatSimple("notification:%s; event=<%s>, pkg=%s, usr=%d",
                TimeUtils.formatDuration(timestampMs), notificationEventToString(event),
                packageName, userId);
    }

    @NonNull
    private static String notificationEventToString(@NotificationEventType int event) {
        switch (event) {
            case NOTIFICATION_EVENT_TYPE_POSTED:
                return "posted";
            case NOTIFICATION_EVENT_TYPE_UPDATED:
                return "updated";
            case NOTIFICATION_EVENT_TYPE_CANCELLED:
                return "cancelled";
            default:
                return String.valueOf(event);
        }
    }

    public static final class BroadcastEvent implements Data {
        public int sourceUid;
        public int targetUserId;
        public int targetUidProcessState;
        public String targetPackage;
        public long idForResponseEvent;
        public long timestampMs;

        @Override
        public void reset() {
            targetPackage = null;
        }

        @Override
        public String toString() {
            return getBroadcastDispatchEventLog(sourceUid, targetPackage, targetUserId,
                    idForResponseEvent, timestampMs, targetUidProcessState);
        }
    }

    public static final class NotificationEvent implements Data {
        public int type;
        public String packageName;
        public int userId;
        public long timestampMs;

        @Override
        public void reset() {
            packageName = null;
        }

        @Override
        public String toString() {
            return getNotificationEventLog(type, packageName, userId, timestampMs);
        }
    }

    public interface Data {
        void reset();
    }
}
+23 −49
Original line number Diff line number Diff line
@@ -16,10 +16,6 @@

package com.android.server.usage;

import static android.app.ActivityManager.procStateToString;

import static com.android.server.usage.UsageStatsService.DEBUG_RESPONSE_STATS;

import android.annotation.ElapsedRealtimeLong;
import android.annotation.IntDef;
import android.annotation.IntRange;
@@ -29,11 +25,9 @@ import android.annotation.UserIdInt;
import android.app.ActivityManager.ProcessState;
import android.app.usage.BroadcastResponseStats;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.LongSparseArray;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.IndentingPrintWriter;
@@ -44,19 +38,19 @@ import java.util.ArrayList;
import java.util.List;

class BroadcastResponseStatsTracker {
    private static final String TAG = "ResponseStatsTracker";
    static final String TAG = "ResponseStatsTracker";

    @Retention(RetentionPolicy.SOURCE)
    @IntDef(prefix = {"NOTIFICATION_EVENT"}, value = {
            NOTIFICATION_EVENT_POSTED,
            NOTIFICATION_EVENT_UPDATED,
            NOTIFICATION_EVENT_CANCELLED
    @IntDef(prefix = {"NOTIFICATION_EVENT_TYPE_"}, value = {
            NOTIFICATION_EVENT_TYPE_POSTED,
            NOTIFICATION_EVENT_TYPE_UPDATED,
            NOTIFICATION_EVENT_TYPE_CANCELLED
    })
    public @interface NotificationEvent {}
    public @interface NotificationEventType {}

    private static final int NOTIFICATION_EVENT_POSTED = 0;
    private static final int NOTIFICATION_EVENT_UPDATED = 1;
    private static final int NOTIFICATION_EVENT_CANCELLED = 2;
    static final int NOTIFICATION_EVENT_TYPE_POSTED = 0;
    static final int NOTIFICATION_EVENT_TYPE_UPDATED = 1;
    static final int NOTIFICATION_EVENT_TYPE_CANCELLED = 2;

    private final Object mLock = new Object();

@@ -76,21 +70,19 @@ class BroadcastResponseStatsTracker {
            new SparseArray<>();

    private AppStandbyInternal mAppStandby;
    private BroadcastResponseStatsLogger mLogger;

    BroadcastResponseStatsTracker(@NonNull AppStandbyInternal appStandby) {
        mAppStandby = appStandby;
        mLogger = new BroadcastResponseStatsLogger();
    }

    // TODO (206518114): Move all callbacks handling to a handler thread.
    void reportBroadcastDispatchEvent(int sourceUid, @NonNull String targetPackage,
            UserHandle targetUser, long idForResponseEvent,
            @ElapsedRealtimeLong long timestampMs, @ProcessState int targetUidProcState) {
        if (DEBUG_RESPONSE_STATS) {
            Slog.d(TAG, TextUtils.formatSimple("reportBroadcastDispatchEvent; "
                            + "srcUid=%d, tgtPkg=%s, tgtUsr=%d, id=%d, ts=%s, state=%s",
                    sourceUid, targetPackage, targetUser, idForResponseEvent,
                    TimeUtils.formatDuration(timestampMs), procStateToString(targetUidProcState)));
        }
        mLogger.logBroadcastDispatchEvent(sourceUid, targetPackage, targetUser,
                idForResponseEvent, timestampMs, targetUidProcState);
        if (targetUidProcState <= mAppStandby.getBroadcastResponseFgThresholdState()) {
            // No need to track the broadcast response state while the target app is
            // in the foreground.
@@ -110,29 +102,23 @@ class BroadcastResponseStatsTracker {

    void reportNotificationPosted(@NonNull String packageName, UserHandle user,
            @ElapsedRealtimeLong long timestampMs) {
        reportNotificationEvent(NOTIFICATION_EVENT_POSTED, packageName, user, timestampMs);
        reportNotificationEvent(NOTIFICATION_EVENT_TYPE_POSTED, packageName, user, timestampMs);
    }

    void reportNotificationUpdated(@NonNull String packageName, UserHandle user,
            @ElapsedRealtimeLong long timestampMs) {
        reportNotificationEvent(NOTIFICATION_EVENT_UPDATED, packageName, user, timestampMs);
        reportNotificationEvent(NOTIFICATION_EVENT_TYPE_UPDATED, packageName, user, timestampMs);

    }

    void reportNotificationCancelled(@NonNull String packageName, UserHandle user,
            @ElapsedRealtimeLong long timestampMs) {
        reportNotificationEvent(NOTIFICATION_EVENT_CANCELLED, packageName, user, timestampMs);
        reportNotificationEvent(NOTIFICATION_EVENT_TYPE_CANCELLED, packageName, user, timestampMs);
    }

    private void reportNotificationEvent(@NotificationEvent int event,
    private void reportNotificationEvent(@NotificationEventType int event,
            @NonNull String packageName, UserHandle user, @ElapsedRealtimeLong long timestampMs) {
        if (DEBUG_RESPONSE_STATS) {
            Slog.d(TAG, TextUtils.formatSimple(
                    "reportNotificationEvent; event=<%s>, pkg=%s, usr=%d, ts=%s",
                    notificationEventToString(event), packageName, user.getIdentifier(),
                    TimeUtils.formatDuration(timestampMs)));
        }
        // TODO (206518114): Store last N events to dump for debugging purposes.
        mLogger.logNotificationEvent(event, packageName, user, timestampMs);
        synchronized (mLock) {
            final LongSparseArray<BroadcastEvent> broadcastEvents =
                    getBroadcastEventsLocked(packageName, user);
@@ -157,13 +143,13 @@ class BroadcastResponseStatsTracker {
                        continue;
                    }
                    switch (event) {
                        case NOTIFICATION_EVENT_POSTED:
                        case NOTIFICATION_EVENT_TYPE_POSTED:
                            responseStats.incrementNotificationsPostedCount(1);
                            break;
                        case NOTIFICATION_EVENT_UPDATED:
                        case NOTIFICATION_EVENT_TYPE_UPDATED:
                            responseStats.incrementNotificationsUpdatedCount(1);
                            break;
                        case NOTIFICATION_EVENT_CANCELLED:
                        case NOTIFICATION_EVENT_TYPE_CANCELLED:
                            responseStats.incrementNotificationsCancelledCount(1);
                            break;
                        default:
@@ -329,20 +315,6 @@ class BroadcastResponseStatsTracker {
        return userResponseStats.getOrCreateBroadcastResponseStats(broadcastEvent);
    }

    @NonNull
    private String notificationEventToString(@NotificationEvent int event) {
        switch (event) {
            case NOTIFICATION_EVENT_POSTED:
                return "posted";
            case NOTIFICATION_EVENT_UPDATED:
                return "updated";
            case NOTIFICATION_EVENT_CANCELLED:
                return "cancelled";
            default:
                return String.valueOf(event);
        }
    }

    void dump(@NonNull IndentingPrintWriter ipw) {
        ipw.println("Broadcast response stats:");
        ipw.increaseIndent();
@@ -351,6 +323,8 @@ class BroadcastResponseStatsTracker {
            dumpBroadcastEventsLocked(ipw);
            ipw.println();
            dumpResponseStatsLocked(ipw);
            ipw.println();
            mLogger.dumpLogs(ipw);
        }

        ipw.decreaseIndent();