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

Commit 02ec70aa authored by Jeff Sharkey's avatar Jeff Sharkey
Browse files

BroadcastQueue: apply singleton policy at entry.

We originally intended for BroadcastSkipPolicy to make its skipping
verdicts without any side-effects, but logic that mutated manifest
receivers targeted at "singleton" processes accidentally leaked in.

This change adds tests to confirm the expected behavior for an
example singleton process, and then lifts the mutating logic into
a place where it can be applied early and consistently.

Bug: 244706927
Test: atest FrameworksMockingServicesTests:BroadcastQueueTest
Test: atest FrameworksMockingServicesTests:BroadcastQueueModernImplTest
Change-Id: Iadeacabaafe5bf7267ff242f439729907e0fe32b
parent 4568a30b
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -288,6 +288,7 @@ class BroadcastProcessQueue {
        mActiveIndex = 0;
        mActiveCountSinceIdle = 0;
        mActiveViaColdStart = false;
        invalidateRunnableAt();
    }

    public void traceProcessStartingBegin() {
+2 −0
Original line number Diff line number Diff line
@@ -232,6 +232,8 @@ public class BroadcastQueueImpl extends BroadcastQueue {
    }

    public void enqueueBroadcastLocked(BroadcastRecord r) {
        r.applySingletonPolicy(mService);

        final boolean replacePending = (r.intent.getFlags()
                & Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;

+2 −0
Original line number Diff line number Diff line
@@ -483,6 +483,8 @@ class BroadcastQueueModernImpl extends BroadcastQueue {

    @Override
    public void enqueueBroadcastLocked(@NonNull BroadcastRecord r) {
        r.applySingletonPolicy(mService);

        // TODO: handle empty receivers to deliver result immediately
        if (r.receivers == null) return;

+29 −0
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ import android.os.IBinder;
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
@@ -715,6 +716,34 @@ final class BroadcastRecord extends Binder {
        return didSomething;
    }

    /**
     * Apply special treatment to manifest receivers hosted by a singleton
     * process, by re-targeting them at {@link UserHandle#USER_SYSTEM}.
     */
    void applySingletonPolicy(@NonNull ActivityManagerService service) {
        if (receivers == null) return;
        for (int i = 0; i < receivers.size(); i++) {
            final Object receiver = receivers.get(i);
            if (receiver instanceof ResolveInfo) {
                final ResolveInfo info = (ResolveInfo) receiver;
                boolean isSingleton = false;
                try {
                    isSingleton = service.isSingleton(info.activityInfo.processName,
                            info.activityInfo.applicationInfo,
                            info.activityInfo.name, info.activityInfo.flags);
                } catch (SecurityException e) {
                    BroadcastQueue.logw(e.getMessage());
                }
                final int receiverUid = info.activityInfo.applicationInfo.uid;
                if (callingUid != android.os.Process.SYSTEM_UID && isSingleton
                        && service.isValidSingletonCall(callingUid, receiverUid)) {
                    info.activityInfo = service.getActivityInfoForUser(info.activityInfo,
                            UserHandle.USER_SYSTEM);
                }
            }
        }
    }

    @Override
    public String toString() {
        if (cachedToString == null) {
+0 −18
Original line number Diff line number Diff line
@@ -137,15 +137,6 @@ public class BroadcastSkipPolicy {
            }
        }

        boolean isSingleton = false;
        try {
            isSingleton = mService.isSingleton(info.activityInfo.processName,
                    info.activityInfo.applicationInfo,
                    info.activityInfo.name, info.activityInfo.flags);
        } catch (SecurityException e) {
            Slog.w(TAG, e.getMessage());
            return true;
        }
        if ((info.activityInfo.flags&ActivityInfo.FLAG_SINGLE_USER) != 0) {
            if (ActivityManager.checkUidPermission(
                    android.Manifest.permission.INTERACT_ACROSS_USERS,
@@ -216,15 +207,6 @@ public class BroadcastSkipPolicy {
            return true;
        }

        // This is safe to do even if we are skipping the broadcast, and we need
        // this information now to evaluate whether it is going to be allowed to run.
        final int receiverUid = info.activityInfo.applicationInfo.uid;
        // If it's a singleton, it needs to be the same app or a special app
        if (r.callingUid != Process.SYSTEM_UID && isSingleton
                && mService.isValidSingletonCall(r.callingUid, receiverUid)) {
            info.activityInfo = mService.getActivityInfoForUser(info.activityInfo, 0);
        }

        final int allowed = mService.getAppStartModeLOSP(
                info.activityInfo.applicationInfo.uid, info.activityInfo.packageName,
                info.activityInfo.applicationInfo.targetSdkVersion, -1, true, false, false);
Loading