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

Commit 484d03d5 authored by Jeff Sharkey's avatar Jeff Sharkey Committed by Sudheer Shanka
Browse files

BroadcastQueue: blocked registered receivers.

In most situations, it's reasonable to shortcut and "assumeDelivered"
for registered receivers as a fire-and-forget operation, since if
that app is wedged it'll only impact delivery of future broadcasts
to that app.

However, for ordered and resultTo broadcasts, there are
often other apps that need to be "blocked" until the receiver actually
finishes.  If we "assumeDelivered" in those cases, we'd be subjecting
those other apps to race conditions.

To resolve this, we tighten "assumeDelivered" for these situations,
which also ensure we detect ANRs that would block delivery for other
apps, and add tests to verify.

Bug: 257972988
Test: atest FrameworksMockingServicesTests:BroadcastRecordTest
Test: atest FrameworksMockingServicesTests:BroadcastQueueTest
Test: atest FrameworksMockingServicesTests:BroadcastQueueModernImplTest
Change-Id: I492aa9ff6f7797e64245c2073db8e2ecddfd8544
parent 326f9b5c
Loading
Loading
Loading
Loading
+25 −9
Original line number Diff line number Diff line
@@ -784,9 +784,10 @@ public final class ActivityThread extends ClientTransactionHandler

    static final class ReceiverData extends BroadcastReceiver.PendingResult {
        public ReceiverData(Intent intent, int resultCode, String resultData, Bundle resultExtras,
                boolean ordered, boolean sticky, IBinder token, int sendingUser) {
                boolean ordered, boolean sticky, boolean assumeDelivered, IBinder token,
                int sendingUser) {
            super(resultCode, resultData, resultExtras, TYPE_COMPONENT, ordered, sticky,
                    token, sendingUser, intent.getFlags());
                    assumeDelivered, token, sendingUser, intent.getFlags());
            this.intent = intent;
        }

@@ -1040,10 +1041,10 @@ public final class ActivityThread extends ClientTransactionHandler

        public final void scheduleReceiver(Intent intent, ActivityInfo info,
                CompatibilityInfo compatInfo, int resultCode, String data, Bundle extras,
                boolean sync, int sendingUser, int processState) {
                boolean ordered, boolean assumeDelivered, int sendingUser, int processState) {
            updateProcessState(processState, false);
            ReceiverData r = new ReceiverData(intent, resultCode, data, extras,
                    sync, false, mAppThread.asBinder(), sendingUser);
                    ordered, false, assumeDelivered, mAppThread.asBinder(), sendingUser);
            r.info = info;
            sendMessage(H.RECEIVER, r);
        }
@@ -1054,11 +1055,11 @@ public final class ActivityThread extends ClientTransactionHandler
                if (r.registered) {
                    scheduleRegisteredReceiver(r.receiver, r.intent,
                            r.resultCode, r.data, r.extras, r.ordered, r.sticky,
                            r.sendingUser, r.processState);
                            r.assumeDelivered, r.sendingUser, r.processState);
                } else {
                    scheduleReceiver(r.intent, r.activityInfo, r.compatInfo,
                            r.resultCode, r.data, r.extras, r.sync,
                            r.sendingUser, r.processState);
                            r.assumeDelivered, r.sendingUser, r.processState);
                }
            }
        }
@@ -1288,10 +1289,25 @@ public final class ActivityThread extends ClientTransactionHandler
        // applies transaction ordering per object for such calls.
        public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
                int resultCode, String dataStr, Bundle extras, boolean ordered,
                boolean sticky, int sendingUser, int processState) throws RemoteException {
                boolean sticky, boolean assumeDelivered, int sendingUser, int processState)
                throws RemoteException {
            updateProcessState(processState, false);
            receiver.performReceive(intent, resultCode, dataStr, extras, ordered,
                    sticky, sendingUser);

            // We can't modify IIntentReceiver due to UnsupportedAppUsage, so
            // try our best to shortcut to known subclasses, and alert if
            // registered using a custom IIntentReceiver that isn't able to
            // report an expected delivery event
            if (receiver instanceof LoadedApk.ReceiverDispatcher.InnerReceiver) {
                ((LoadedApk.ReceiverDispatcher.InnerReceiver) receiver).performReceive(intent,
                        resultCode, dataStr, extras, ordered, sticky, assumeDelivered, sendingUser);
            } else {
                if (!assumeDelivered) {
                    Log.wtf(TAG, "scheduleRegisteredReceiver() called for " + receiver
                            + " and " + intent + " without mechanism to finish delivery");
                }
                receiver.performReceive(intent, resultCode, dataStr, extras, ordered, sticky,
                        sendingUser);
            }
        }

        @Override
+3 −3
Original line number Diff line number Diff line
@@ -65,8 +65,8 @@ import java.util.Map;
oneway interface IApplicationThread {
    void scheduleReceiver(in Intent intent, in ActivityInfo info,
            in CompatibilityInfo compatInfo,
            int resultCode, in String data, in Bundle extras, boolean sync,
            int sendingUser, int processState);
            int resultCode, in String data, in Bundle extras, boolean ordered,
            boolean assumeDelivered, int sendingUser, int processState);

    void scheduleReceiverList(in List<ReceiverInfo> info);

@@ -102,7 +102,7 @@ oneway interface IApplicationThread {
            in String[] args);
    void scheduleRegisteredReceiver(IIntentReceiver receiver, in Intent intent,
            int resultCode, in String data, in Bundle extras, boolean ordered,
            boolean sticky, int sendingUser, int processState);
            boolean sticky, boolean assumeDelivered, int sendingUser, int processState);
    void scheduleLowMemory();
    void profilerControl(boolean start, in ProfilerInfo profilerInfo, int profileType);
    void setSchedulingGroup(int group);
+28 −27
Original line number Diff line number Diff line
@@ -1679,6 +1679,16 @@ public final class LoadedApk {
            @Override
            public void performReceive(Intent intent, int resultCode, String data,
                    Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
                Log.wtf(TAG, "performReceive() called targeting raw IIntentReceiver for " + intent);
                performReceive(intent, resultCode, data, extras, ordered, sticky,
                        BroadcastReceiver.PendingResult.guessAssumeDelivered(
                                BroadcastReceiver.PendingResult.TYPE_REGISTERED, ordered),
                        sendingUser);
            }

            public void performReceive(Intent intent, int resultCode, String data,
                    Bundle extras, boolean ordered, boolean sticky, boolean assumeDelivered,
                    int sendingUser) {
                final LoadedApk.ReceiverDispatcher rd;
                if (intent == null) {
                    Log.wtf(TAG, "Null intent received");
@@ -1693,8 +1703,8 @@ public final class LoadedApk {
                }
                if (rd != null) {
                    rd.performReceive(intent, resultCode, data, extras,
                            ordered, sticky, sendingUser);
                } else {
                            ordered, sticky, assumeDelivered, sendingUser);
                } else if (!assumeDelivered) {
                    // The activity manager dispatched a broadcast to a registered
                    // receiver in this process, but before it could be delivered the
                    // receiver was unregistered.  Acknowledge the broadcast on its
@@ -1729,30 +1739,26 @@ public final class LoadedApk {

        final class Args extends BroadcastReceiver.PendingResult {
            private Intent mCurIntent;
            private final boolean mOrdered;
            private boolean mDispatched;
            private boolean mRunCalled;

            public Args(Intent intent, int resultCode, String resultData, Bundle resultExtras,
                    boolean ordered, boolean sticky, int sendingUser) {
                    boolean ordered, boolean sticky, boolean assumeDelivered, int sendingUser) {
                super(resultCode, resultData, resultExtras,
                        mRegistered ? TYPE_REGISTERED : TYPE_UNREGISTERED, ordered,
                        sticky, mAppThread.asBinder(), sendingUser, intent.getFlags());
                        sticky, assumeDelivered, mAppThread.asBinder(), sendingUser,
                        intent.getFlags());
                mCurIntent = intent;
                mOrdered = ordered;
            }

            public final Runnable getRunnable() {
                return () -> {
                    final BroadcastReceiver receiver = mReceiver;
                    final boolean ordered = mOrdered;

                    if (ActivityThread.DEBUG_BROADCAST) {
                        int seq = mCurIntent.getIntExtra("seq", -1);
                        Slog.i(ActivityThread.TAG, "Dispatching broadcast " + mCurIntent.getAction()
                                + " seq=" + seq + " to " + mReceiver);
                        Slog.i(ActivityThread.TAG, "  mRegistered=" + mRegistered
                                + " mOrderedHint=" + ordered);
                    }

                    final IActivityManager mgr = ActivityManager.getService();
@@ -1766,11 +1772,9 @@ public final class LoadedApk {
                    mDispatched = true;
                    mRunCalled = true;
                    if (receiver == null || intent == null || mForgotten) {
                        if (mRegistered && ordered) {
                        if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                                "Finishing null broadcast to " + mReceiver);
                        sendFinished(mgr);
                        }
                        return;
                    }

@@ -1790,11 +1794,9 @@ public final class LoadedApk {
                        receiver.setPendingResult(this);
                        receiver.onReceive(mContext, intent);
                    } catch (Exception e) {
                        if (mRegistered && ordered) {
                        if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                                "Finishing failed broadcast to " + mReceiver);
                        sendFinished(mgr);
                        }
                        if (mInstrumentation == null ||
                                !mInstrumentation.onException(mReceiver, e)) {
                            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
@@ -1868,9 +1870,10 @@ public final class LoadedApk {
        }

        public void performReceive(Intent intent, int resultCode, String data,
                Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
                Bundle extras, boolean ordered, boolean sticky, boolean assumeDelivered,
                int sendingUser) {
            final Args args = new Args(intent, resultCode, data, extras, ordered,
                    sticky, sendingUser);
                    sticky, assumeDelivered, sendingUser);
            if (intent == null) {
                Log.wtf(TAG, "Null intent received");
            } else {
@@ -1881,14 +1884,12 @@ public final class LoadedApk {
                }
            }
            if (intent == null || !mActivityThread.post(args.getRunnable())) {
                if (mRegistered && ordered) {
                IActivityManager mgr = ActivityManager.getService();
                if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                        "Finishing sync broadcast to " + mReceiver);
                args.sendFinished(mgr);
            }
        }
        }

    }

+1 −0
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ parcelable ReceiverInfo {
    Intent intent;
    String data;
    Bundle extras;
    boolean assumeDelivered;
    int sendingUser;
    int processState;
    int resultCode;
+33 −8
Original line number Diff line number Diff line
@@ -82,6 +82,7 @@ public abstract class BroadcastReceiver {
        final boolean mOrderedHint;
        @UnsupportedAppUsage
        final boolean mInitialStickyHint;
        final boolean mAssumeDeliveredHint;
        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
        final IBinder mToken;
        @UnsupportedAppUsage
@@ -105,17 +106,38 @@ public abstract class BroadcastReceiver {
        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
        public PendingResult(int resultCode, String resultData, Bundle resultExtras, int type,
                boolean ordered, boolean sticky, IBinder token, int userId, int flags) {
            this(resultCode, resultData, resultExtras, type, ordered, sticky,
                    guessAssumeDelivered(type, ordered), token, userId, flags);
        }

        /** @hide */
        public PendingResult(int resultCode, String resultData, Bundle resultExtras, int type,
                boolean ordered, boolean sticky, boolean assumeDelivered, IBinder token,
                int userId, int flags) {
            mResultCode = resultCode;
            mResultData = resultData;
            mResultExtras = resultExtras;
            mType = type;
            mOrderedHint = ordered;
            mInitialStickyHint = sticky;
            mAssumeDeliveredHint = assumeDelivered;
            mToken = token;
            mSendingUser = userId;
            mFlags = flags;
        }

        /** @hide */
        public static boolean guessAssumeDelivered(int type, boolean ordered) {
            // When a caller didn't provide a concrete way of knowing if we need
            // to report delivery, make a best-effort guess
            if (type == TYPE_COMPONENT) {
                return false;
            } else if (ordered && type != TYPE_UNREGISTERED) {
                return false;
            }
            return true;
        }

        /**
         * Version of {@link BroadcastReceiver#setResultCode(int)
         * BroadcastReceiver.setResultCode(int)} for
@@ -252,7 +274,7 @@ public abstract class BroadcastReceiver {
                            "Finishing broadcast to component " + mToken);
                    sendFinished(mgr);
                }
            } else if (mOrderedHint && mType != TYPE_UNREGISTERED) {
            } else {
                if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                        "Finishing broadcast to " + mToken);
                final IActivityManager mgr = ActivityManager.getService();
@@ -279,14 +301,17 @@ public abstract class BroadcastReceiver {
                    if (mResultExtras != null) {
                        mResultExtras.setAllowFds(false);
                    }

                    // When the OS didn't assume delivery, we need to inform
                    // it that we've actually finished the delivery
                    if (!mAssumeDeliveredHint) {
                        if (mOrderedHint) {
                            am.finishReceiver(mToken, mResultCode, mResultData, mResultExtras,
                                    mAbortBroadcast, mFlags);
                        } else {
                        // This broadcast was sent to a component; it is not ordered,
                        // but we still need to tell the activity manager we are done.
                            am.finishReceiver(mToken, 0, null, null, false, mFlags);
                        }
                    }
                } catch (RemoteException ex) {
                }
            }
Loading