blob: c1f1dfd4fe7539747d3451b671a31f36d2c8576b [file] [log] [blame]
/*
* 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 com.android.server.am;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UptimeMillisLong;
import android.content.ContentResolver;
import android.content.Intent;
import android.os.Bundle;
import android.os.DropBoxManager;
import android.os.Handler;
import android.os.Trace;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.server.DropBoxManagerInternal;
import com.android.server.LocalServices;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.util.Objects;
import java.util.Set;
/**
* Queue of broadcast intents and associated bookkeeping.
*/
public abstract class BroadcastQueue {
public static final String TAG = "BroadcastQueue";
public static final String TAG_DUMP = "broadcast_queue_dump";
final @NonNull ActivityManagerService mService;
final @NonNull Handler mHandler;
final @NonNull BroadcastSkipPolicy mSkipPolicy;
final @NonNull BroadcastHistory mHistory;
final @NonNull String mQueueName;
BroadcastQueue(@NonNull ActivityManagerService service, @NonNull Handler handler,
@NonNull String name, @NonNull BroadcastSkipPolicy skipPolicy,
@NonNull BroadcastHistory history) {
mService = Objects.requireNonNull(service);
mHandler = Objects.requireNonNull(handler);
mQueueName = Objects.requireNonNull(name);
mSkipPolicy = Objects.requireNonNull(skipPolicy);
mHistory = Objects.requireNonNull(history);
}
static void logw(@NonNull String msg) {
Slog.w(TAG, msg);
}
static void logv(@NonNull String msg) {
Slog.v(TAG, msg);
}
static void checkState(boolean expression, @NonNull String msg) {
if (!expression) {
throw new IllegalStateException(msg);
}
}
static void checkStateWtf(boolean expression, @NonNull String msg) {
if (!expression) {
Slog.wtf(TAG, new IllegalStateException(msg));
}
}
static int traceBegin(@NonNull String methodName) {
final int cookie = methodName.hashCode();
Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
TAG, methodName, cookie);
return cookie;
}
static void traceEnd(int cookie) {
Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,
TAG, cookie);
}
@Override
public String toString() {
return mQueueName;
}
public abstract void start(@NonNull ContentResolver resolver);
public abstract boolean isDelayBehindServices();
/**
* Return the preferred scheduling group for the given process, typically
* influenced by a broadcast being actively dispatched.
*
* @return scheduling group such as {@link ProcessList#SCHED_GROUP_DEFAULT},
* otherwise {@link ProcessList#SCHED_GROUP_UNDEFINED} if this queue
* has no opinion.
*/
@GuardedBy("mService")
public abstract int getPreferredSchedulingGroupLocked(@NonNull ProcessRecord app);
/**
* Enqueue the given broadcast to be eventually dispatched.
* <p>
* Callers must populate {@link BroadcastRecord#receivers} with the relevant
* targets before invoking this method.
* <p>
* When {@link Intent#FLAG_RECEIVER_REPLACE_PENDING} is set, this method
* internally handles replacement of any matching broadcasts.
*/
@GuardedBy("mService")
public abstract void enqueueBroadcastLocked(@NonNull BroadcastRecord r);
/**
* Signal delivered back from the given process to indicate that it's
* finished processing the current broadcast being dispatched to it.
* <p>
* If this signal isn't delivered back in a timely fashion, we assume the
* receiver has somehow wedged and we trigger an ANR.
*/
@GuardedBy("mService")
public abstract boolean finishReceiverLocked(@NonNull ProcessRecord app, int resultCode,
@Nullable String resultData, @Nullable Bundle resultExtras, boolean resultAbort,
boolean waitForServices);
@GuardedBy("mService")
public abstract void backgroundServicesFinishedLocked(int userId);
/**
* Signal from OS internals that the given process has just been actively
* attached, and is ready to begin receiving broadcasts.
*
* @return if the queue performed an action on the given process, such as
* dispatching a pending broadcast
*/
@GuardedBy("mService")
public abstract boolean onApplicationAttachedLocked(@NonNull ProcessRecord app)
throws BroadcastDeliveryFailedException;
/**
* Signal from OS internals that the given process has timed out during
* an attempted start and attachment.
*/
@GuardedBy("mService")
public abstract void onApplicationTimeoutLocked(@NonNull ProcessRecord app);
/**
* Signal from OS internals that the given process, which had already been
* previously attached, has now encountered a problem such as crashing or
* not responding.
*/
@GuardedBy("mService")
public abstract void onApplicationProblemLocked(@NonNull ProcessRecord app);
/**
* Signal from OS internals that the given process has been killed, and is
* no longer actively running.
*/
@GuardedBy("mService")
public abstract void onApplicationCleanupLocked(@NonNull ProcessRecord app);
/**
* Signal from OS internals that the given process is in a freezable state and will be
* frozen soon after.
*/
@GuardedBy("mService")
public abstract void onProcessFreezableChangedLocked(@NonNull ProcessRecord app);
/**
* Signal from OS internals that the given package (or some subset of that
* package) has been disabled or uninstalled, and that any pending
* broadcasts should be cleaned up.
*/
@GuardedBy("mService")
public abstract boolean cleanupDisabledPackageReceiversLocked(@Nullable String packageName,
@Nullable Set<String> filterByClasses, int userId);
/**
* Quickly determine if this queue has broadcasts that are still waiting to
* be delivered at some point in the future.
*
* @see #waitForIdle
* @see #waitForBarrier
*/
@GuardedBy("mService")
public abstract boolean isIdleLocked();
/**
* Quickly determine if this queue has non-deferred broadcasts enqueued before the given
* barrier timestamp that are still waiting to be delivered.
*
* @see #waitForIdle
* @see #waitForBarrier
*/
@GuardedBy("mService")
public abstract boolean isBeyondBarrierLocked(@UptimeMillisLong long barrierTime);
/**
* Quickly determine if this queue has non-deferred broadcasts waiting to be dispatched,
* that match {@code intent}, as defined by {@link Intent#filterEquals(Intent)}.
*
* @see #waitForDispatched(Intent, PrintWriter)
*/
@GuardedBy("mService")
public abstract boolean isDispatchedLocked(@NonNull Intent intent);
/**
* Wait until this queue becomes completely idle.
* <p>
* Any broadcasts waiting to be delivered at some point in the future will
* be dispatched as quickly as possible.
* <p>
* Callers are cautioned that the queue may take a long time to go idle,
* since running apps can continue sending new broadcasts in perpetuity;
* consider using {@link #waitForBarrier} instead.
*/
public abstract void waitForIdle(@NonNull PrintWriter pw);
/**
* Wait until any currently waiting non-deferred broadcasts have been dispatched.
* <p>
* Any broadcasts waiting to be delivered at some point in the future will
* be dispatched as quickly as possible.
* <p>
* Callers are advised that this method will <em>not</em> wait for any
* future broadcasts that are newly enqueued after being invoked.
*/
public abstract void waitForBarrier(@NonNull PrintWriter pw);
/**
* Wait until all non-deferred broadcasts matching {@code intent}, as defined by
* {@link Intent#filterEquals(Intent)}, have been dispatched.
* <p>
* Any broadcasts waiting to be delivered at some point in the future will
* be dispatched as quickly as possible.
*/
public abstract void waitForDispatched(@NonNull Intent intent, @NonNull PrintWriter pw);
/**
* Delays delivering broadcasts to the specified package.
*
* <p> Note that this is only valid for modern queue.
*/
public void forceDelayBroadcastDelivery(@NonNull String targetPackage,
long delayedDurationMs) {
// No default implementation.
}
/**
* Brief summary of internal state, useful for debugging purposes.
*/
@GuardedBy("mService")
public abstract @NonNull String describeStateLocked();
@GuardedBy("mService")
public abstract void dumpDebug(@NonNull ProtoOutputStream proto, long fieldId);
@GuardedBy("mService")
public abstract boolean dumpLocked(@NonNull FileDescriptor fd, @NonNull PrintWriter pw,
@NonNull String[] args, int opti, boolean dumpConstants, boolean dumpHistory,
boolean dumpAll, @Nullable String dumpPackage, boolean needSep);
/**
* Execute {@link #dumpLocked} and store the output into
* {@link DropBoxManager} for later inspection.
*/
public void dumpToDropBoxLocked(@Nullable String msg) {
LocalServices.getService(DropBoxManagerInternal.class).addEntry(TAG_DUMP, (fd) -> {
try (FileOutputStream out = new FileOutputStream(fd);
PrintWriter pw = new PrintWriter(out)) {
pw.print("Message: ");
pw.println(msg);
dumpLocked(fd, pw, null, 0, false, false, false, null, false);
pw.flush();
}
}, DropBoxManager.IS_TEXT);
}
}