Loading core/api/system-current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -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); core/java/android/app/BroadcastOptions.java +34 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.app; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; Loading Loading @@ -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. Loading Loading @@ -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; Loading Loading @@ -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); } /** Loading Loading @@ -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) Loading Loading @@ -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; } } services/core/java/com/android/server/am/BroadcastQueue.java +44 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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)) { Loading Loading
core/api/system-current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -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);
core/java/android/app/BroadcastOptions.java +34 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.app; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; Loading Loading @@ -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. Loading Loading @@ -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; Loading Loading @@ -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); } /** Loading Loading @@ -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) Loading Loading @@ -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; } }
services/core/java/com/android/server/am/BroadcastQueue.java +44 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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)) { Loading