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

Commit f577ac06 authored by Jeff Sharkey's avatar Jeff Sharkey
Browse files

[9/?] Reduce BroadcastQueue interface complexity.

Adds some additional methods to BroadcastRecord to aid in upcoming
per-process queue changes.

Add UidObserver with default no-op implementation to aid readability
of child classes.

Updates unit tests to acquire relevant locks.

Bug: 243656033
Test: atest CtsContentTestCases:BroadcastReceiverTest
Test: atest FrameworksMockingServicesTests:BroadcastQueueTest
Change-Id: I2ff1e868586861e4dcd6586ad22139ba84eaf39c
parent 8200d406
Loading
Loading
Loading
Loading
+50 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.app;

/**
 * Default implementation of {@link IUidObserver} for which all methods are
 * no-op. Subclasses can override the select methods they're interested in
 * handling.
 *
 * @hide
 */
public class UidObserver extends IUidObserver.Stub {
    @Override
    public void onUidActive(int uid) {
    }

    @Override
    public void onUidCachedChanged(int uid, boolean cached) {
    }

    @Override
    public void onUidGone(int uid, boolean disabled) {
    }

    @Override
    public void onUidIdle(int uid, boolean disabled) {
    }

    @Override
    public void onUidProcAdjChanged(int uid) {
    }

    @Override
    public void onUidStateChanged(int uid, int procState, long procStateSeq, int capability) {
    }
}
+12 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import android.content.ContentResolver;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;

import com.android.internal.annotations.GuardedBy;
@@ -59,6 +60,16 @@ public abstract class BroadcastQueue {
        mConstants.startObserving(mHandler, resolver);
    }

    static void checkState(boolean state, String msg) {
        if (!state) {
            Slog.wtf(TAG, msg, new Throwable());
        }
    }

    static void logv(String msg) {
        Slog.v(TAG, msg);
    }

    @Override
    public String toString() {
        return mQueueName;
@@ -74,6 +85,7 @@ public abstract class BroadcastQueue {
     *         otherwise {@link ProcessList#SCHED_GROUP_UNDEFINED} if this queue
     *         has no opinion.
     */
    @GuardedBy("mService")
    public abstract int getPreferredSchedulingGroupLocked(@NonNull ProcessRecord app);

    /**
+1 −12
Original line number Diff line number Diff line
@@ -1409,7 +1409,7 @@ public class BroadcastQueueImpl extends BroadcastQueue {
                info.activityInfo.applicationInfo, true,
                r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
                new HostingRecord(HostingRecord.HOSTING_TYPE_BROADCAST, r.curComponent,
                        r.intent.getAction(), getHostingRecordTriggerType(r)),
                        r.intent.getAction(), r.getHostingRecordTriggerType()),
                isActivityCapable ? ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE : ZYGOTE_POLICY_FLAG_EMPTY,
                (r.intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false);
        if (r.curApp == null) {
@@ -1432,17 +1432,6 @@ public class BroadcastQueueImpl extends BroadcastQueue {
        mPendingBroadcastRecvIndex = recIdx;
    }

    private String getHostingRecordTriggerType(BroadcastRecord r) {
        if (r.alarm) {
            return HostingRecord.TRIGGER_TYPE_ALARM;
        } else if (r.pushMessage) {
            return HostingRecord.TRIGGER_TYPE_PUSH_MESSAGE;
        } else if (r.pushMessageOverQuota) {
            return HostingRecord.TRIGGER_TYPE_PUSH_MESSAGE_OVER_QUOTA;
        }
        return HostingRecord.TRIGGER_TYPE_UNKNOWN;
    }

    @Nullable
    private String getTargetPackage(BroadcastRecord r) {
        if (r.intent == null) {
+96 −1
Original line number Diff line number Diff line
@@ -84,6 +84,7 @@ final class BroadcastRecord extends Binder {
    final BroadcastOptions options; // BroadcastOptions supplied by caller
    final List receivers;   // contains BroadcastFilter and ResolveInfo
    final int[] delivery;   // delivery state of each receiver
    final long[] scheduledTime; // uptimeMillis when each receiver was scheduled
    final long[] duration;   // duration a receiver took to process broadcast
    IIntentReceiver resultTo; // who receives final result if non-null
    boolean deferred;
@@ -127,10 +128,18 @@ final class BroadcastRecord extends Binder {
    static final int CALL_DONE_RECEIVE = 3;
    static final int WAITING_SERVICES = 4;

    /** Initial state: waiting to run in future */
    static final int DELIVERY_PENDING = 0;
    /** Terminal state: finished successfully */
    static final int DELIVERY_DELIVERED = 1;
    /** Terminal state: skipped due to internal policy */
    static final int DELIVERY_SKIPPED = 2;
    /** Terminal state: timed out during attempted delivery */
    static final int DELIVERY_TIMEOUT = 3;
    /** Intermediate state: currently executing */
    static final int DELIVERY_SCHEDULED = 4;
    /** Terminal state: failure to dispatch */
    static final int DELIVERY_FAILURE = 5;

    ProcessRecord curApp;       // hosting application of current receiver.
    ComponentName curComponent; // the receiver class that is currently running.
@@ -253,6 +262,8 @@ final class BroadcastRecord extends Binder {
                case DELIVERY_DELIVERED: pw.print("Deliver"); break;
                case DELIVERY_SKIPPED:   pw.print("Skipped"); break;
                case DELIVERY_TIMEOUT:   pw.print("Timeout"); break;
                case DELIVERY_SCHEDULED: pw.print("Schedul"); break;
                case DELIVERY_FAILURE:   pw.print("Failure"); break;
                default:                 pw.print("???????"); break;
            }
            pw.print(" "); TimeUtils.formatDuration(duration[i], pw);
@@ -300,6 +311,7 @@ final class BroadcastRecord extends Binder {
        options = _options;
        receivers = _receivers;
        delivery = new int[_receivers != null ? _receivers.size() : 0];
        scheduledTime = new long[delivery.length];
        duration = new long[delivery.length];
        resultTo = _resultTo;
        resultCode = _resultCode;
@@ -346,6 +358,7 @@ final class BroadcastRecord extends Binder {
        options = from.options;
        receivers = from.receivers;
        delivery = from.delivery;
        scheduledTime = from.scheduledTime;
        duration = from.duration;
        resultTo = from.resultTo;
        enqueueTime = from.enqueueTime;
@@ -497,7 +510,77 @@ final class BroadcastRecord extends Binder {
        return ret;
    }

    int getReceiverUid(Object receiver) {
    /**
     * Update the delivery state of the given {@link #receivers} index.
     * Automatically updates any time measurements related to state changes.
     */
    void setDeliveryState(int index, int deliveryState) {
        delivery[index] = deliveryState;

        switch (deliveryState) {
            case DELIVERY_DELIVERED:
                duration[index] = SystemClock.uptimeMillis() - scheduledTime[index];
                break;
            case DELIVERY_SCHEDULED:
                scheduledTime[index] = SystemClock.uptimeMillis();
                break;
        }
    }

    boolean isForeground() {
        return (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0;
    }

    boolean isReplacePending() {
        return (intent.getFlags() & Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;
    }

    @NonNull String getHostingRecordTriggerType() {
        if (alarm) {
            return HostingRecord.TRIGGER_TYPE_ALARM;
        } else if (pushMessage) {
            return HostingRecord.TRIGGER_TYPE_PUSH_MESSAGE;
        } else if (pushMessageOverQuota) {
            return HostingRecord.TRIGGER_TYPE_PUSH_MESSAGE_OVER_QUOTA;
        }
        return HostingRecord.TRIGGER_TYPE_UNKNOWN;
    }

    /**
     * Return an instance of {@link #intent} specialized for the given receiver.
     * For example, this returns a new specialized instance if the extras need
     * to be filtered, or a {@link ResolveInfo} needs to be configured.
     *
     * @return a specialized intent, otherwise {@code null} to indicate that the
     *         broadcast should not be delivered to this receiver, typically due
     *         to it being filtered away by {@link #filterExtrasForReceiver}.
     */
    @Nullable Intent getReceiverIntent(@NonNull Object receiver) {
        Intent newIntent = null;
        if (filterExtrasForReceiver != null) {
            final Bundle extras = intent.getExtras();
            if (extras != null) {
                final int receiverUid = getReceiverUid(receiver);
                final Bundle filteredExtras = filterExtrasForReceiver.apply(receiverUid, extras);
                if (filteredExtras == null) {
                    // Completely filtered; skip the broadcast!
                    return null;
                } else {
                    newIntent = new Intent(intent);
                    newIntent.replaceExtras(filteredExtras);
                }
            }
        }
        if (receiver instanceof ResolveInfo) {
            if (newIntent == null) {
                newIntent = new Intent(intent);
            }
            newIntent.setComponent(((ResolveInfo) receiver).activityInfo.getComponentName());
        }
        return (newIntent != null) ? newIntent : intent;
    }

    static int getReceiverUid(@NonNull Object receiver) {
        if (receiver instanceof BroadcastFilter) {
            return ((BroadcastFilter) receiver).owningUid;
        } else /* if (receiver instanceof ResolveInfo) */ {
@@ -505,6 +588,14 @@ final class BroadcastRecord extends Binder {
        }
    }

    static String getReceiverProcessName(@NonNull Object receiver) {
        if (receiver instanceof BroadcastFilter) {
            return ((BroadcastFilter) receiver).receiverList.app.processName;
        } else /* if (receiver instanceof ResolveInfo) */ {
            return ((ResolveInfo) receiver).activityInfo.processName;
        }
    }

    public BroadcastRecord maybeStripForHistory() {
        if (!intent.canStripForHistory()) {
            return this;
@@ -561,6 +652,10 @@ final class BroadcastRecord extends Binder {
            + " u" + userId + " " + intent.getAction() + "}";
    }

    public String toShortString() {
        return intent.getAction() + "/u" + userId;
    }

    public void dumpDebug(ProtoOutputStream proto, long fieldId) {
        long token = proto.start(fieldId);
        proto.write(BroadcastRecordProto.USER_ID, userId);
+12 −2
Original line number Diff line number Diff line
@@ -16,7 +16,6 @@

package com.android.server.am;

import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
import static com.android.server.am.ActivityManagerService.checkComponentPermission;
import static com.android.server.am.BroadcastQueue.TAG;
@@ -35,7 +34,6 @@ import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.PermissionInfo;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -57,6 +55,18 @@ public class BroadcastSkipPolicy {
        mService = service;
    }

    /**
     * Determine if the given {@link BroadcastRecord} is eligible to be sent to
     * the given {@link BroadcastFilter} or {@link ResolveInfo}.
     */
    public boolean shouldSkip(@NonNull BroadcastRecord r, @NonNull Object target) {
        if (target instanceof BroadcastFilter) {
            return shouldSkip(r, (BroadcastFilter) target);
        } else {
            return shouldSkip(r, (ResolveInfo) target);
        }
    }

    /**
     * Determine if the given {@link BroadcastRecord} is eligible to be sent to
     * the given {@link ResolveInfo}.
Loading