| /* |
| * 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); |
| } |
| } |