blob: 8f8990fdaae7997d6c3336ca006b6b299b0ed418 [file] [log] [blame]
/*
* Copyright (C) 2020 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 static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import android.content.pm.ApplicationInfo;
import android.os.SystemClock;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.server.wm.WindowProcessController;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* The helper class to handle no response process. An independent thread will be created on demand
* so the caller can report the ANR without worrying about taking long time.
*/
class AnrHelper {
private static final String TAG = TAG_WITH_CLASS_NAME ? "AnrHelper" : TAG_AM;
/**
* If the system is extremely slow somehow that the ANR has been pending too long for more than
* this time, the information might be outdated. So we only the dump the unresponsive process
* instead of including other processes to avoid making the system more busy.
*/
private static final long EXPIRED_REPORT_TIME_MS = TimeUnit.MINUTES.toMillis(1);
@GuardedBy("mAnrRecords")
private final ArrayList<AnrRecord> mAnrRecords = new ArrayList<>();
private final AtomicBoolean mRunning = new AtomicBoolean(false);
void appNotResponding(ProcessRecord anrProcess, String annotation) {
appNotResponding(anrProcess, null /* activityShortComponentName */, null /* aInfo */,
null /* parentShortComponentName */, null /* parentProcess */,
false /* aboveSystem */, annotation);
}
void appNotResponding(ProcessRecord anrProcess, String activityShortComponentName,
ApplicationInfo aInfo, String parentShortComponentName,
WindowProcessController parentProcess, boolean aboveSystem, String annotation) {
synchronized (mAnrRecords) {
mAnrRecords.add(new AnrRecord(anrProcess, activityShortComponentName, aInfo,
parentShortComponentName, parentProcess, aboveSystem, annotation));
}
startAnrConsumerIfNeeded();
}
private void startAnrConsumerIfNeeded() {
if (mRunning.compareAndSet(false, true)) {
new AnrConsumerThread().start();
}
}
/**
* The thread to execute {@link ProcessRecord#appNotResponding}. It will terminate if all
* records are handled.
*/
private class AnrConsumerThread extends Thread {
AnrConsumerThread() {
super("AnrConsumer");
}
private AnrRecord next() {
synchronized (mAnrRecords) {
return mAnrRecords.isEmpty() ? null : mAnrRecords.remove(0);
}
}
@Override
public void run() {
AnrRecord r;
while ((r = next()) != null) {
final long startTime = SystemClock.uptimeMillis();
// If there are many ANR at the same time, the latency may be larger. If the latency
// is too large, the stack trace might not be meaningful.
final long reportLatency = startTime - r.mTimestamp;
final boolean onlyDumpSelf = reportLatency > EXPIRED_REPORT_TIME_MS;
r.appNotResponding(onlyDumpSelf);
final long endTime = SystemClock.uptimeMillis();
Slog.d(TAG, "Completed ANR of " + r.mApp.processName + " in "
+ (endTime - startTime) + "ms, latency " + reportLatency
+ (onlyDumpSelf ? "ms (expired, only dump ANR app)" : "ms"));
}
mRunning.set(false);
synchronized (mAnrRecords) {
// The race should be unlikely to happen. Just to make sure we don't miss.
if (!mAnrRecords.isEmpty()) {
startAnrConsumerIfNeeded();
}
}
}
}
private static class AnrRecord {
final ProcessRecord mApp;
final String mActivityShortComponentName;
final String mParentShortComponentName;
final String mAnnotation;
final ApplicationInfo mAppInfo;
final WindowProcessController mParentProcess;
final boolean mAboveSystem;
final long mTimestamp = SystemClock.uptimeMillis();
AnrRecord(ProcessRecord anrProcess, String activityShortComponentName,
ApplicationInfo aInfo, String parentShortComponentName,
WindowProcessController parentProcess, boolean aboveSystem, String annotation) {
mApp = anrProcess;
mActivityShortComponentName = activityShortComponentName;
mParentShortComponentName = parentShortComponentName;
mAnnotation = annotation;
mAppInfo = aInfo;
mParentProcess = parentProcess;
mAboveSystem = aboveSystem;
}
void appNotResponding(boolean onlyDumpSelf) {
mApp.appNotResponding(mActivityShortComponentName, mAppInfo,
mParentShortComponentName, mParentProcess, mAboveSystem, mAnnotation,
onlyDumpSelf);
}
}
}