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

Commit 6ec5f7fd authored by Sudheer Shanka's avatar Sudheer Shanka Committed by Android (Google) Code Review
Browse files

Merge "Inform UsageStatsManager about broadcast-dispatched events."

parents eafe47b9 c2e7bfb7
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -744,6 +744,7 @@ package android.app {
    method public void clearRequireCompatChange();
    method public boolean isPendingIntentBackgroundActivityLaunchAllowed();
    method public static android.app.BroadcastOptions makeBasic();
    method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void recordResponseEventWhileInBackground(@IntRange(from=0) long);
    method @RequiresPermission(android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND) public void setBackgroundActivityStartsAllowed(boolean);
    method public void setDontSendToRestrictedApps(boolean);
    method public void setPendingIntentBackgroundActivityLaunchAllowed(boolean);
+34 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.app;

import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -52,6 +53,7 @@ public class BroadcastOptions extends ComponentOptions {
    private String[] mRequireNoneOfPermissions;
    private long mRequireCompatChangeId = CHANGE_INVALID;
    private boolean mRequireCompatChangeEnabled = true;
    private long mIdForResponseEvent;

    /**
     * Change ID which is invalid.
@@ -164,6 +166,12 @@ public class BroadcastOptions extends ComponentOptions {
    public static final int TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED =
            PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED;

    /**
     * Corresponds to {@link #recordResponseEventWhileInBackground(long)}.
     */
    private static final String KEY_ID_FOR_RESPONSE_EVENT =
            "android:broadcast.idForResponseEvent";

    public static BroadcastOptions makeBasic() {
        BroadcastOptions opts = new BroadcastOptions();
        return opts;
@@ -198,6 +206,7 @@ public class BroadcastOptions extends ComponentOptions {
        mRequireNoneOfPermissions = opts.getStringArray(KEY_REQUIRE_NONE_OF_PERMISSIONS);
        mRequireCompatChangeId = opts.getLong(KEY_REQUIRE_COMPAT_CHANGE_ID, CHANGE_INVALID);
        mRequireCompatChangeEnabled = opts.getBoolean(KEY_REQUIRE_COMPAT_CHANGE_ENABLED, true);
        mIdForResponseEvent = opts.getLong(KEY_ID_FOR_RESPONSE_EVENT);
    }

    /**
@@ -510,6 +519,28 @@ public class BroadcastOptions extends ComponentOptions {
        }
    }

    /**
     * Sets whether events (such as posting a notification) originating from an app after it
     * receives the broadcast while in background should be recorded as responses to the broadcast.
     *
     * @param id ID to be used for the response events corresponding to this broadcast. If the
     *           value is {@code 0} (default), then response events will not be recorded. Otherwise,
     *           they will be recorded with the ID provided.
     *
     * @hide
     */
    @SystemApi
    @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS)
    public void recordResponseEventWhileInBackground(@IntRange(from = 0) long id) {
        mIdForResponseEvent = id;
    }

    /** @hide */
    @IntRange(from = 0)
    public long getIdForResponseEvent() {
        return mIdForResponseEvent;
    }

    /**
     * Returns the created options as a Bundle, which can be passed to
     * {@link android.content.Context#sendBroadcast(android.content.Intent)
@@ -549,6 +580,9 @@ public class BroadcastOptions extends ComponentOptions {
            b.putLong(KEY_REQUIRE_COMPAT_CHANGE_ID, mRequireCompatChangeId);
            b.putBoolean(KEY_REQUIRE_COMPAT_CHANGE_ENABLED, mRequireCompatChangeEnabled);
        }
        if (mIdForResponseEvent != 0) {
            b.putLong(KEY_ID_FOR_RESPONSE_EVENT, mIdForResponseEvent);
        }
        return b.isEmpty() ? null : b;
    }
}
+44 −0
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PERMISSIONS
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BROADCAST;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_MU;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.AppGlobals;
@@ -37,6 +38,7 @@ import android.app.IApplicationThread;
import android.app.PendingIntent;
import android.app.RemoteServiceException.CannotDeliverBroadcastException;
import android.app.usage.UsageEvents.Event;
import android.app.usage.UsageStatsManagerInternal;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.IIntentReceiver;
@@ -69,6 +71,7 @@ import android.util.proto.ProtoOutputStream;

import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.LocalServices;

import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -325,6 +328,7 @@ public final class BroadcastQueue {
        mService.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_START_RECEIVER);

        // Tell the application to launch this receiver.
        maybeReportBroadcastDispatchedEventLocked(r);
        r.intent.setComponent(r.curComponent);

        boolean started = false;
@@ -917,6 +921,7 @@ public final class BroadcastQueue {
                r.receiverTime = SystemClock.uptimeMillis();
                maybeAddAllowBackgroundActivityStartsToken(filter.receiverList.app, r);
                maybeScheduleTempAllowlistLocked(filter.owningUid, r, r.options);
                maybeReportBroadcastDispatchedEventLocked(r);
                performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
                        new Intent(r.intent), r.resultCode, r.resultData,
                        r.resultExtras, r.ordered, r.initialSticky, r.userId);
@@ -1831,6 +1836,45 @@ public final class BroadcastQueue {
        mPendingBroadcastRecvIndex = recIdx;
    }


    @Nullable
    private String getTargetPackage(BroadcastRecord r) {
        if (r.intent == null) {
            return null;
        }
        if (r.intent.getPackage() != null) {
            return r.intent.getPackage();
        } else if (r.intent.getComponent() != null) {
            return r.intent.getComponent().getPackageName();
        }
        return null;
    }

    private void maybeReportBroadcastDispatchedEventLocked(BroadcastRecord r) {
        final String targetPackage = getTargetPackage(r);
        // Ignore non-explicit broadcasts
        if (targetPackage == null) {
            return;
        }
        // TODO (206518114): Only allow apps with ACCESS_PACKAGE_USAGE_STATS to set
        // getIdForResponseEvent.
        if (r.options == null || r.options.getIdForResponseEvent() <= 0) {
            return;
        }
        // TODO (206518114): Only report this event when the broadcast is dispatched while the app
        // is in the background state.
        getUsageStatsManagerInternal().reportBroadcastDispatched(
                r.callingUid, targetPackage, UserHandle.of(r.userId),
                r.options.getIdForResponseEvent(), SystemClock.elapsedRealtime());
    }

    @NonNull
    private UsageStatsManagerInternal getUsageStatsManagerInternal() {
        final UsageStatsManagerInternal usageStatsManagerInternal =
                LocalServices.getService(UsageStatsManagerInternal.class);
        return usageStatsManagerInternal;
    }

    private boolean noteOpForManifestReceiver(int appOp, BroadcastRecord r, ResolveInfo info,
            ComponentName component) {
        if (ArrayUtils.isEmpty(info.activityInfo.attributionTags)) {