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

Commit 751214da authored by Xin Guan's avatar Xin Guan
Browse files

Introduce user interaction usage events reporting

Introduce a new dimension to the user interaction event
by offering additional user interaction details.

Bug: 296061232
Test: atest
CtsUsageStatsTestCases:android.app.usage.cts.UsageStatsTest
      atest FrameworksCoreTests:android.app.usage.UsageStatsPersistenceTest
      atest
      FrameworksServicesTests:com.android.server.usage.UsageStatsDatabaseTest

Change-Id: I75446f219ae859853b4243d74db50f83b2fba31e
parent 1a058643
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -9342,6 +9342,7 @@ package android.app.usage {
    method public String getClassName();
    method public android.content.res.Configuration getConfiguration();
    method public int getEventType();
    method @FlaggedApi("android.app.usage.user_interaction_type_api") @NonNull public android.os.PersistableBundle getExtras();
    method public String getPackageName();
    method public String getShortcutId();
    method public long getTimeStamp();
@@ -9407,6 +9408,8 @@ package android.app.usage {
    method @FlaggedApi("android.app.usage.filter_based_event_query_api") @NonNull @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public android.app.usage.UsageEvents queryEvents(@NonNull android.app.usage.UsageEventsQuery);
    method public android.app.usage.UsageEvents queryEventsForSelf(long, long);
    method public java.util.List<android.app.usage.UsageStats> queryUsageStats(int, long, long);
    field @FlaggedApi("android.app.usage.user_interaction_type_api") public static final String EXTRA_EVENT_ACTION = "android.app.usage.extra.EVENT_ACTION";
    field @FlaggedApi("android.app.usage.user_interaction_type_api") public static final String EXTRA_EVENT_CATEGORY = "android.app.usage.extra.EVENT_CATEGORY";
    field public static final int INTERVAL_BEST = 4; // 0x4
    field public static final int INTERVAL_DAILY = 0; // 0x0
    field public static final int INTERVAL_MONTHLY = 2; // 0x2
+3 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import android.app.usage.BroadcastResponseStatsList;
import android.app.usage.UsageEvents;
import android.app.usage.UsageEventsQuery;
import android.content.pm.ParceledListSlice;
import android.os.PersistableBundle;

/**
 * System private API for talking with the UsageStatsManagerService.
@@ -77,6 +78,8 @@ interface IUsageStatsManager {
            String callingPackage);
    void reportUsageStop(in IBinder activity, String token, String callingPackage);
    void reportUserInteraction(String packageName, int userId);
    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.REPORT_USAGE_STATS)")
    void reportUserInteractionWithBundle(String packageName, int userId, in PersistableBundle eventExtras);
    int getUsageSource();
    void forceUsageSourceSettingRead();
    long getLastTimeAnyComponentUsed(String packageName, String callingPackage);
+14 −0
Original line number Diff line number Diff line
@@ -223,6 +223,7 @@ public final class ParcelableUsageEventList implements Parcelable {
        event.mContentAnnotations = null;
        event.mNotificationChannelId = null;
        event.mLocusId = null;
        event.mExtras = null;

        switch (event.mEventType) {
            case Event.CONFIGURATION_CHANGE -> {
@@ -237,6 +238,11 @@ public final class ParcelableUsageEventList implements Parcelable {
            case Event.STANDBY_BUCKET_CHANGED -> event.mBucketAndReason = in.readInt();
            case Event.NOTIFICATION_INTERRUPTION -> event.mNotificationChannelId = in.readString();
            case Event.LOCUS_ID_SET -> event.mLocusId = in.readString();
            case Event.USER_INTERACTION -> {
                if (in.readInt() != 0) {
                    event.mExtras = in.readPersistableBundle(getClass().getClassLoader());
                }
            }
        }
        event.mFlags = in.readInt();

@@ -263,6 +269,14 @@ public final class ParcelableUsageEventList implements Parcelable {
            case Event.STANDBY_BUCKET_CHANGED -> dest.writeInt(event.mBucketAndReason);
            case Event.NOTIFICATION_INTERRUPTION -> dest.writeString(event.mNotificationChannelId);
            case Event.LOCUS_ID_SET -> dest.writeString(event.mLocusId);
            case Event.USER_INTERACTION -> {
                if (event.mExtras != null) {
                    dest.writeInt(1);
                    dest.writePersistableBundle(event.mExtras);
                } else {
                    dest.writeInt(0);
                }
            }
        }
        dest.writeInt(event.mFlags);
    }
+49 −0
Original line number Diff line number Diff line
@@ -16,7 +16,9 @@
package android.app.usage;

import android.annotation.CurrentTimeMillisLong;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
@@ -24,6 +26,7 @@ import android.content.res.Configuration;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.PersistableBundle;
import android.util.Log;

import java.lang.annotation.Retention;
@@ -550,6 +553,22 @@ public final class UsageEvents implements Parcelable {
         */
        public int mLocusIdToken = UNASSIGNED_TOKEN;

        /** @hide */
        public PersistableBundle mExtras = null;

        /** @hide */
        public static class UserInteractionEventExtrasToken {
            public int mCategoryToken = UNASSIGNED_TOKEN;
            public int mActionToken = UNASSIGNED_TOKEN;

            public UserInteractionEventExtrasToken() {
                // Do nothing.
            }
        }

        /** @hide */
        public UserInteractionEventExtrasToken mUserInteractionExtrasToken = null;

        /** @hide */
        @EventFlags
        public int mFlags;
@@ -649,6 +668,21 @@ public final class UsageEvents implements Parcelable {
            return mEventType;
        }

        /**
         * Retrieves a map of extended data from the event if the event is of type
         * {@link #USER_INTERACTION}.
         *
         * @return the map of all extras that associated with the reported user interaction
         *         event. The returned {@link PersistableBundle} will contain the extras
         *         {@link UsageStatsManager#EXTRA_EVENT_CATEGORY} and
         *         {@link UsageStatsManager#EXTRA_EVENT_ACTION}. {@link PersistableBundle#EMPTY}
         *         will be returned if the details are not available.
         */
        @FlaggedApi(Flags.FLAG_USER_INTERACTION_TYPE_API)
        public @NonNull PersistableBundle getExtras() {
            return mExtras == null ? PersistableBundle.EMPTY : mExtras;
        }

        /**
         * Returns a {@link Configuration} for this event if the event is of type
         * {@link #CONFIGURATION_CHANGE}, otherwise it returns null.
@@ -747,6 +781,7 @@ public final class UsageEvents implements Parcelable {
            mBucketAndReason = orig.mBucketAndReason;
            mNotificationChannelId = orig.mNotificationChannelId;
            mLocusId = orig.mLocusId;
            mExtras = orig.mExtras;
        }
    }

@@ -987,6 +1022,14 @@ public final class UsageEvents implements Parcelable {
            case Event.LOCUS_ID_SET:
                p.writeString(event.mLocusId);
                break;
            case Event.USER_INTERACTION:
                if (event.mExtras != null) {
                    p.writeInt(1);
                    p.writePersistableBundle(event.mExtras);
                } else {
                    p.writeInt(0);
                }
                break;
        }
        p.writeInt(event.mFlags);
    }
@@ -1036,6 +1079,7 @@ public final class UsageEvents implements Parcelable {
        eventOut.mContentAnnotations = null;
        eventOut.mNotificationChannelId = null;
        eventOut.mLocusId = null;
        eventOut.mExtras = null;

        switch (eventOut.mEventType) {
            case Event.CONFIGURATION_CHANGE:
@@ -1059,6 +1103,11 @@ public final class UsageEvents implements Parcelable {
            case Event.LOCUS_ID_SET:
                eventOut.mLocusId = p.readString();
                break;
            case Event.USER_INTERACTION:
                if (p.readInt() != 0) {
                    eventOut.mExtras = p.readPersistableBundle(getClass().getClassLoader());
                }
                break;
        }
        eventOut.mFlags = p.readInt();
    }
+58 −6
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.annotation.UserHandleAware;
import android.annotation.UserIdInt;
import android.app.Activity;
import android.app.BroadcastOptions;
import android.app.PendingIntent;
@@ -35,6 +36,7 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.pm.ParceledListSlice;
import android.os.Build;
import android.os.PersistableBundle;
import android.os.PowerWhitelistManager;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -392,6 +394,23 @@ public final class UsageStatsManager {
    @SystemApi
    public static final String EXTRA_TIME_USED = "android.app.usage.extra.TIME_USED";

    /**
     * A String extra, when used with {@link UsageEvents.Event#getExtras}, that indicates
     * the category of the user interaction associated with the event. The category cannot
     * be more than 127 characters, longer value will be truncated to 127 characters.
     */
    @FlaggedApi(Flags.FLAG_USER_INTERACTION_TYPE_API)
    public static final String EXTRA_EVENT_CATEGORY =
            "android.app.usage.extra.EVENT_CATEGORY";

    /**
     * A String extra, when used with {@link UsageEvents.Event#getExtras}, that indicates
     * the action of the user interaction associated with the event. The action cannot be
     * more than 127 characters, longer value will be truncated to 127 characters.
     */
    @FlaggedApi(Flags.FLAG_USER_INTERACTION_TYPE_API)
    public static final String EXTRA_EVENT_ACTION =
            "android.app.usage.extra.EVENT_ACTION";

    /**
     * App usage observers will consider the task root package the source of usage.
@@ -1128,6 +1147,7 @@ public final class UsageStatsManager {
     * Reports user interaction with a given package in the given user.
     *
     * <p><em>This method is only for use by the system</em>
     *
     * @hide
     */
    @RequiresPermission(android.Manifest.permission.REPORT_USAGE_STATS)
@@ -1139,6 +1159,38 @@ public final class UsageStatsManager {
        }
    }

    /**
     * Reports user interaction with given package and a particular {@code extras}
     * in the given user.
     *
     * <p>
     * Note: The structure of {@code extras} is a {@link PersistableBundle} with the
     * category {@link #EXTRA_EVENT_CATEGORY} and the action {@link #EXTRA_EVENT_ACTION}.
     * Category provides additional detail about the user interaction, the value
     * is defined in namespace based. Example: android.app.notification could be used to
     * indicate that the reported user interaction is related to notification. Action
     * indicates the general action that performed.
     * </p>
     *
     * @param packageName The package name of the app
     * @param userId The user id who triggers the user interaction
     * @param extras The {@link PersistableBundle} that will be used to specify the
     *               extra details for the user interaction event. The {@link PersistableBundle}
     *               must contain the extras {@link #EXTRA_EVENT_CATEGORY},
     *               {@link #EXTRA_EVENT_ACTION}. Cannot be empty.
     * @hide
     */
    @FlaggedApi(Flags.FLAG_USER_INTERACTION_TYPE_API)
    @RequiresPermission(android.Manifest.permission.REPORT_USAGE_STATS)
    public void reportUserInteraction(@NonNull String packageName, @UserIdInt int userId,
            @NonNull PersistableBundle extras) {
        try {
            mService.reportUserInteractionWithBundle(packageName, userId, extras);
        } catch (RemoteException re) {
            throw re.rethrowFromSystemServer();
        }
    }

    /**
     * Report usage associated with a particular {@code token} has started. Tokens are app defined
     * strings used to represent usage of in-app features. Apps with the {@link
Loading