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

Commit 803eab69 authored by Amith Yamasani's avatar Amith Yamasani
Browse files

Add a new usage event type for seen notification

On a notification becoming visible, NOTIFICATION_SEEN event is
dispatched. This will only bump up the app's bucket to WORKING_SET and
no higher. Same goes for sync adapters associated with a content
provider. Only move them to WORKING_SET.

Updated NotificationManagerService to report event to usagestats on
visibility changes.

Bug: 63527785
Test: runtest -x
frameworks/base/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
atest NotificationManagerServiceTest
Change-Id: I5b132e1fc1f70e2126473b43b9b1979fbc523b85
parent c8e3c7c8
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -100,6 +100,12 @@ public final class UsageEvents implements Parcelable {
         */
        public static final int CHOOSER_ACTION = 9;

        /**
         * An event type denoting that a notification was viewed by the user.
         * @hide
         */
        public static final int NOTIFICATION_SEEN = 10;

        /** @hide */
        public static final int FLAG_IS_PACKAGE_INSTANT_APP = 1 << 0;

+14 −4
Original line number Diff line number Diff line
@@ -736,6 +736,11 @@ public class NotificationManagerService extends SystemService {
                for (NotificationVisibility nv : newlyVisibleKeys) {
                    NotificationRecord r = mNotificationsByKey.get(nv.key);
                    if (r == null) continue;
                    if (!r.isSeen()) {
                        // Report to usage stats that notification was made visible
                        if (DBG) Slog.d(TAG, "Marking notification as visible " + nv.key);
                        reportSeen(r);
                    }
                    r.setVisibility(true, nv.rank);
                    nv.recycle();
                }
@@ -1643,6 +1648,14 @@ public class NotificationManagerService extends SystemService {
        return INotificationManager.Stub.asInterface(mService);
    }

    protected void reportSeen(NotificationRecord r) {
        final int userId = r.sbn.getUserId();
        mAppUsageStats.reportEvent(r.sbn.getPackageName(),
                userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM
                        : userId,
                UsageEvents.Event.NOTIFICATION_SEEN);
    }

    @VisibleForTesting
    NotificationManagerInternal getInternalService() {
        return mInternalService;
@@ -2269,10 +2282,7 @@ public class NotificationManagerService extends SystemService {
                            }
                            if (!r.isSeen()) {
                                if (DBG) Slog.d(TAG, "Marking notification as seen " + keys[i]);
                                mAppUsageStats.reportEvent(r.sbn.getPackageName(),
                                        userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM
                                                : userId,
                                        UsageEvents.Event.USER_INTERACTION);
                                reportSeen(r);
                                r.setSeen();
                            }
                        }
+8 −1
Original line number Diff line number Diff line
@@ -144,7 +144,9 @@ public class NotificationManagerServiceTest extends NotificationTestCase {

    // Use a Testable subclass so we can simulate calls from the system without failing.
    private static class TestableNotificationManagerService extends NotificationManagerService {
        public TestableNotificationManagerService(Context context) { super(context); }
        public TestableNotificationManagerService(Context context) {
            super(context);
        }

        @Override
        protected boolean isCallingUidSystem() {
@@ -160,6 +162,11 @@ public class NotificationManagerServiceTest extends NotificationTestCase {
        protected ICompanionDeviceManager getCompanionManager() {
            return null;
        }

        @Override
        protected void reportSeen(NotificationRecord r) {
            return;
        }
    }

    @Before
+32 −10
Original line number Diff line number Diff line
@@ -17,6 +17,9 @@
package com.android.server.usage;

import static android.app.usage.AppStandby.*;
import static android.app.usage.UsageEvents.Event.NOTIFICATION_SEEN;
import static android.app.usage.UsageEvents.Event.USER_INTERACTION;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
@@ -247,21 +250,27 @@ public class AppStandbyControllerTests {
                        false));
    }

    private void reportEvent(AppStandbyController controller, long elapsedTime) {
    private void reportEvent(AppStandbyController controller, int eventType,
            long elapsedTime) {
        // Back to ACTIVE on event
        UsageEvents.Event ev = new UsageEvents.Event();
        ev.mPackage = PACKAGE_1;
        ev.mEventType = UsageEvents.Event.USER_INTERACTION;
        ev.mEventType = eventType;
        controller.reportEvent(ev, elapsedTime, USER_ID);
    }

    private int getStandbyBucket(AppStandbyController controller) {
        return controller.getAppStandbyBucket(PACKAGE_1, USER_ID, mInjector.mElapsedRealtime,
                true);
    }

    @Test
    public void testBuckets() throws Exception {
        AppStandbyController controller = setupController();

        assertTimeout(controller, 0, STANDBY_BUCKET_NEVER);

        reportEvent(controller, 0);
        reportEvent(controller, USER_INTERACTION, 0);

        // ACTIVE bucket
        assertTimeout(controller, 11 * HOUR_MS, STANDBY_BUCKET_ACTIVE);
@@ -278,7 +287,7 @@ public class AppStandbyControllerTests {
        // RARE bucket
        assertTimeout(controller, 9 * DAY_MS, STANDBY_BUCKET_RARE);

        reportEvent(controller, 9 * DAY_MS);
        reportEvent(controller, USER_INTERACTION, 9 * DAY_MS);

        assertTimeout(controller, 9 * DAY_MS, STANDBY_BUCKET_ACTIVE);

@@ -293,7 +302,7 @@ public class AppStandbyControllerTests {

        assertTimeout(controller, 0, STANDBY_BUCKET_NEVER);

        reportEvent(controller, 0);
        reportEvent(controller, USER_INTERACTION, 0);

        // ACTIVE bucket
        assertTimeout(controller, 11 * HOUR_MS, STANDBY_BUCKET_ACTIVE);
@@ -304,9 +313,7 @@ public class AppStandbyControllerTests {
        // RARE bucket, should fail because the screen wasn't ON.
        mInjector.mElapsedRealtime = 9 * DAY_MS;
        controller.checkIdleStates(USER_ID);
        assertNotEquals(STANDBY_BUCKET_RARE,
                controller.getAppStandbyBucket(PACKAGE_1, USER_ID, mInjector.mElapsedRealtime,
                false));
        assertNotEquals(STANDBY_BUCKET_RARE, getStandbyBucket(controller));

        mInjector.setDisplayOn(true);
        assertTimeout(controller, 18 * DAY_MS, STANDBY_BUCKET_RARE);
@@ -318,8 +325,7 @@ public class AppStandbyControllerTests {
        setChargingState(controller, false);

        controller.forceIdleState(PACKAGE_1, USER_ID, true);
        assertEquals(STANDBY_BUCKET_RARE, controller.getAppStandbyBucket(PACKAGE_1, USER_ID, 0,
                true));
        assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(controller));
        assertTrue(controller.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));

        controller.forceIdleState(PACKAGE_1, USER_ID, false);
@@ -327,4 +333,20 @@ public class AppStandbyControllerTests {
                true));
        assertFalse(controller.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
    }

    @Test
    public void testNotificationEvent() throws Exception {
        AppStandbyController controller = setupController();
        setChargingState(controller, false);

        reportEvent(controller, USER_INTERACTION, 0);
        assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(controller));
        mInjector.mElapsedRealtime = 1;
        reportEvent(controller, NOTIFICATION_SEEN, mInjector.mElapsedRealtime);
        assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(controller));

        controller.forceIdleState(PACKAGE_1, USER_ID, true);
        reportEvent(controller, NOTIFICATION_SEEN, mInjector.mElapsedRealtime);
        assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(controller));
    }
}
+26 −5
Original line number Diff line number Diff line
@@ -186,7 +186,7 @@ public class AppIdleHistory {
        writeScreenOnTime();
    }

    public void reportUsage(String packageName, int userId, long elapsedRealtime) {
    public int reportUsage(String packageName, int userId, long elapsedRealtime) {
        ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
        AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName,
                elapsedRealtime, true);
@@ -197,13 +197,34 @@ public class AppIdleHistory {
                + (elapsedRealtime - mElapsedSnapshot);
        appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime);
        appUsageHistory.recent[HISTORY_SIZE - 1] = FLAG_LAST_STATE | FLAG_PARTIAL_ACTIVE;
        appUsageHistory.currentBucket = AppStandby.STANDBY_BUCKET_ACTIVE;
        if (appUsageHistory.currentBucket > STANDBY_BUCKET_ACTIVE) {
            appUsageHistory.currentBucket = STANDBY_BUCKET_ACTIVE;
            if (DEBUG) {
                Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket
                        + ", reason=" + appUsageHistory.bucketingReason);
            }
        }
        appUsageHistory.bucketingReason = AppStandby.REASON_USAGE;

        return appUsageHistory.currentBucket;
    }

    public int reportMildUsage(String packageName, int userId, long elapsedRealtime) {
        ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
        AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName,
                elapsedRealtime, true);
        if (appUsageHistory.currentBucket > STANDBY_BUCKET_WORKING_SET) {
            appUsageHistory.currentBucket = STANDBY_BUCKET_WORKING_SET;
            if (DEBUG) {
                Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket
                        + ", reason=" + appUsageHistory.bucketingReason);
            }
        }
        // TODO: Should this be a different reason for partial usage?
        appUsageHistory.bucketingReason = AppStandby.REASON_USAGE;

        return appUsageHistory.currentBucket;
    }

    public void setIdle(String packageName, int userId, long elapsedRealtime) {
        ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
Loading