Android 组件ANR超时机制
首先一张大家常用的图
之后查看源码以分析activity的anr原理
代码位置
Frameworkcom\android\server\am\AppErrors.java
final void appNotResponding(ProcessRecord app, ActivityRecord activity,
ActivityRecord parent, boolean aboveSystem, final String annotation) {
...
// Log the ANR to the main log.
StringBuilder info = new StringBuilder();
info.setLength(0);
info.append("ANR in ").append(app.processName);
if (activity != null && activity.shortComponentName != null) {
info.append(" (").append(activity.shortComponentName).append(")");
}
info.append("\n");
info.append("PID: ").append(app.pid).append("\n");
if (annotation != null) {
info.append("Reason: ").append(annotation).append("\n");
}
if (parent != null && parent != activity) {
info.append("Parent: ").append(parent.shortComponentName).append("\n");
}
这里是anr处理的核心类
该方法是应用通过ams调用产生
对应的ams接口
@Override
public void appNotRespondingViaProvider(IBinder connection) {
enforceCallingPermission(REMOVE_TASKS, "appNotRespondingViaProvider()");
final ContentProviderConnection conn = (ContentProviderConnection) connection;
if (conn == null) {
Slog.w(TAG, "ContentProviderConnection is null");
return;
}
final ProcessRecord host = conn.provider.proc;
if (host == null) {
Slog.w(TAG, "Failed to find hosting ProcessRecord");
return;
}
mHandler.post(new Runnable() {
@Override
public void run() {
mAppErrors.appNotResponding(host, null, null, false,
"ContentProvider not responding");
}
});
}
应用的调用位置在
当应用发生anr时调用系统的接口,下面是ActivityThread
final void appNotRespondingViaProvider(IBinder provider) {
synchronized (mProviderMap) {
ProviderRefCount prc = mProviderRefCountMap.get(provider);
if (prc != null) {
try {
ActivityManager.getService()
.appNotRespondingViaProvider(prc.holder.connection);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
}
从这里分成了两个类可以报警anr这里是一个应用端调用的类
android\content\ContentProviderClient.java
private class NotRespondingRunnable implements Runnable {
@Override
public void run() {
Log.w(TAG, "Detected provider not responding: " + mContentProvider);
mContentResolver.appNotRespondingViaProvider(mContentProvider);
}
}
这里看到一个anr线程
sAnrHandler = new Handler(Looper.getMainLooper(), null, true /* async */);
...
private void beforeRemote() {
if (mAnrRunnable != null) {
sAnrHandler.postDelayed(mAnrRunnable, mAnrTimeout);
}
}
private void afterRemote() {
if (mAnrRunnable != null) {
sAnrHandler.removeCallbacks(mAnrRunnable);
}
}
扔给主looper
从这里分成了两个类可以报警anr这里是一个系统端调用的类
android\app\ContextImpl.java
private static final class ApplicationContentResolver extends ContentResolver {
@Override
public void appNotRespondingViaProvider(IContentProvider icp) {
mMainThread.appNotRespondingViaProvider(icp.asBinder());
}
private ContextImpl(@Nullable ContextImpl container, @NonNull ActivityThread mainThread,
@NonNull LoadedApk packageInfo, @Nullable String splitName,
@Nullable IBinder activityToken, @Nullable UserHandle user, int flags,
@Nullable ClassLoader classLoader) {
...
mContentResolver = new ApplicationContentResolver(this, mainThread);
}
@Override
public ContentResolver getContentResolver() {
return mContentResolver;
}
android\app\ApplicationErrorReport.java
public static ComponentName getErrorReportReceiver(Context context,
String packageName, int appFlags) {
// check if error reporting is enabled in secure settings
int enabled = Settings.Global.getInt(context.getContentResolver(),
Settings.Global.SEND_ACTION_APP_ERROR, 0);
...
// if there is a default receiver, try that
candidate = SystemProperties.get(DEFAULT_ERROR_RECEIVER_PROPERTY);
return getErrorReportReceiver(pm, packageName, candidate);
}
对于系统可以调用的又会回到系统的apperror类
void startAppProblemLocked(ProcessRecord app) {
// If this app is not running under the current user, then we
// can't give it a report button because that would require
// launching the report UI under a different user.
app.errorReportReceiver = null;
for (int userId : mService.mUserController.getCurrentProfileIds()) {
if (app.userId == userId) {
app.errorReportReceiver = ApplicationErrorReport.getErrorReportReceiver(
mContext, app.info.packageName, app.info.flags);
}
}
mService.skipCurrentReceiverLocked(app);
}
private boolean handleAppCrashInActivityController(ProcessRecord r,
ApplicationErrorReport.CrashInfo crashInfo,
String shortMsg, String longMsg,
String stackTrace, long timeMillis,
int callingPid, int callingUid) {
if (mService.mController == null) {
return false;
}
try {
String name = r != null ? r.processName : null;
int pid = r != null ? r.pid : callingPid;
int uid = r != null ? r.info.uid : callingUid;
if (!mService.mController.appCrashed(name, pid,
shortMsg, longMsg, timeMillis, crashInfo.stackTrace)) {
if ("1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"))
&& "Native crash".equals(crashInfo.exceptionClassName)) {
Slog.w(TAG, "Skip killing native crashed app " + name
+ "(" + pid + ") during testing");
} else {
Slog.w(TAG, "Force-killing crashed app " + name
+ " at watcher's request");
if (r != null) {
if (!makeAppCrashingLocked(r, shortMsg, longMsg, stackTrace, null))
{
r.kill("crash", true);
}
} else {
// Huh.
Process.killProcess(pid);
ActivityManagerService.killProcessGroup(uid, pid);
}
}
return true;
}
} catch (RemoteException e) {
mService.mController = null;
Watchdog.getInstance().setActivityController(null);
}
return false;
}
这里是最终的调用线程
com\android\server\am\NativeCrashListener.java
class NativeCrashReporter extends Thread {
ProcessRecord mApp;
int mSignal;
String mCrashReport;
NativeCrashReporter(ProcessRecord app, int signal, String report) {
super("NativeCrashReport");
mApp = app;
mSignal = signal;
mCrashReport = report;
}
@Override
public void run() {
try {
CrashInfo ci = new CrashInfo();
ci.exceptionClassName = "Native crash";
ci.exceptionMessage = Os.strsignal(mSignal);
ci.throwFileName = "unknown";
ci.throwClassName = "unknown";
ci.throwMethodName = "unknown";
ci.stackTrace = mCrashReport;
if (DEBUG) Slog.v(TAG, "Calling handleApplicationCrash()");
mAm.handleApplicationCrashInner("native_crash", mApp, mApp.processName, ci);
if (DEBUG) Slog.v(TAG, "<-- handleApplicationCrash() returned");
} catch (Exception e) {
Slog.e(TAG, "Unable to report native crash", e);
}
}
}
该类的调用是在system_server的启动
com\android\server\SystemServer.java
public static void main(String[] args) {
new SystemServer().run();
}
private void run() {
...
// Start services.
try {
traceBeginAndSlog("StartServices");
startBootstrapServices();// 启动引导服务
startCoreServices(); // 启动核心服务
startOtherServices();// 启动其他服务
SystemServerInitThreadPool.shutdown();
} catch (Throwable ex) {
Slog.e("System", "******************************************");
Slog.e("System", "************ Failure starting system services", ex);
throw ex;
} finally {
traceEnd();
}
private void startOtherServices() {
...
mActivityManagerService.systemReady(() -> {
Slog.i(TAG, "Making services ready");
traceBeginAndSlog("StartActivityManagerReadyPhase");
mSystemServiceManager.startBootPhase(
SystemService.PHASE_ACTIVITY_MANAGER_READY);
traceEnd();
traceBeginAndSlog("StartObservingNativeCrashes");
try {
mActivityManagerService.startObservingNativeCrashes();
com\android\server\am\ActivityManagerService.java
public void startObservingNativeCrashes() {
final NativeCrashListener ncl = new NativeCrashListener(this);
ncl.start();
}
com\android\server\am\NativeCrashListener.java
// Must match the path defined in debuggerd.c.
static final String DEBUGGERD_SOCKET_PATH = "/data/system/ndebugsocket";
@Override
public void run() {
final byte[] ackSignal = new byte[1];
if (DEBUG) Slog.i(TAG, "Starting up");
// The file system entity for this socket is created with 0777 perms, owned
// by system:system. selinux restricts things so that only crash_dump can
// access it.
{
File socketFile = new File(DEBUGGERD_SOCKET_PATH);
if (socketFile.exists()) {
socketFile.delete();
}
}
try {
FileDescriptor serverFd = Os.socket(AF_UNIX, SOCK_STREAM, 0);
final UnixSocketAddress sockAddr = UnixSocketAddress.createFileSystem(
DEBUGGERD_SOCKET_PATH);
Os.bind(serverFd, sockAddr);
Os.listen(serverFd, 1);
Os.chmod(DEBUGGERD_SOCKET_PATH, 0777);
读取来自native的anr数据
这个系统的数据从哪里来可以看这篇博客由于源码缺少,等新电脑到了才能继续分析linux core层
http://gityuan.com/2016/06/25/android-native-crash/
系统会生成一个广播
跟踪startAppProblemLocked方法我们会找到ams的调用方法
com\android\server\am\ActiveServices.java
void serviceForegroundCrash(ProcessRecord app, CharSequence serviceRecord) {
mAm.crashApplication(app.uid, app.pid, app.info.packageName, app.userId,
"Context.startForegroundService() did not then call Service.startForeground(): "
+ serviceRecord);
}
其中借鉴
http://gityuan.com/2016/06/25/android-native-crash/
https://blog.csdn.net/CrazyMo_/article/details/123024679
两篇