Inform UsageStatsManager about broadcast-dispatched events.

Bug: 206518114
Test: treehugger
CTS-Coverage-Bug: 206518114
Change-Id: If6b81970461854bf607f2bbd51f47a6f7ec44e03
This commit is contained in:
Sudheer Shanka
2022-01-24 15:10:26 +00:00
parent b7f55ec599
commit c2e7bfb792
3 changed files with 79 additions and 0 deletions

View File

@@ -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);

View File

@@ -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;
}
}

View File

@@ -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)) {