稳定性二——JE流程

1. RuntimeInit.commonInit()

  • 上层应用都是由Zygote fork孵化出来的,分为system_server进程和普通应用进程
  • 进程创建之初会设置未捕获异常的处理器,当系统抛出未捕获的异常时候都会交给异常处理器
  • RuntimeInit.java的commonInit方法设置UncaughtHandler
//frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
  
  protected static final void commonInit() {
        ...
        LoggingHandler loggingHandler = new LoggingHandler();
        RuntimeHooks.setUncaughtExceptionPreHandler(loggingHandler);
        Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler(loggingHandler));
        ...
    }

········
    //libcore/dalvik/src/main/java/dalvik/system/RuntimeHooks.java
    @SystemApi(client = MODULE_LIBRARIES)
    public static void setUncaughtExceptionPreHandler(
            @Nullable Thread.UncaughtExceptionHandler uncaughtExceptionHandler) {
        Thread.setUncaughtExceptionPreHandler(uncaughtExceptionHandler);
    }
···········
    //libcore/ojluni/annotations/hiddenapi/java/lang/Thread.java
    public static void setUncaughtExceptionPreHandler(UncaughtExceptionHandler eh) {
        uncaughtExceptionPreHandler = eh;
    }
//  注意  一个setUncaughtExceptionPreHandler, 一个setDefaultUncaughtExceptionHandler
    public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh) {
        // Android-removed: SecurityManager stubbed out on Android.
        /*
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(
                new RuntimePermission("setDefaultUncaughtExceptionHandler")
                    );
        }
        */

         defaultUncaughtExceptionHandler = eh;
     }

1.1LoggingHandler

UncaughtExceptionHandler只定义了一个接口方法 public void uncaughtException(java.lang.Thread t, java.lang.Throwable e)

    private static class LoggingHandler implements Thread.UncaughtExceptionHandler {
        public volatile boolean mTriggered = false;

        @Override
        public void uncaughtException(Thread t, Throwable e) {
            mTriggered = true;

            // Don't re-enter if KillApplicationHandler has already run
            //已经在crash 流程中,则已经在处理KillApplicationHandler则不再重复进入
            if (mCrashing) return;

            // mApplicationObject is null for non-zygote java programs (e.g. "am")
            // There are also apps running with the system UID. We don't want the
            // first clause in either of these two cases, only for system_server.
            if (mApplicationObject == null && (Process.SYSTEM_UID == Process.myUid())) {
                // sysyem_server进程
                Clog_e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e);
            } else {
                logUncaught(t.getName(), ActivityThread.currentProcessName(), Process.myPid(), e);
            }
        }
    }

........................
    public static void logUncaught(String threadName, String processName, int pid, Throwable e) {
        StringBuilder message = new StringBuilder();
        // The "FATAL EXCEPTION" string is still used on Android even though
        // apps can set a custom UncaughtExceptionHandler that renders uncaught
        // exceptions non-fatal.
       //这个就是日志经常看到的  TAG是AndroidRuntime,打印
        message.append("FATAL EXCEPTION: ").append(threadName).append("\n");
        if (processName != null) {
            message.append("Process: ").append(processName).append(", ");
        }
        message.append("PID: ").append(pid);
        Clog_e(TAG, message.toString(), e);
    }

当线程因为未捕获的异常停止时,Java 虚拟机会调用 uncaughtException() 函数。

可以看出,uncaughtException() 是在crash 最开始调用的,用以输出crash 开头信息

  • 当 system 进程crash提示 *** FATAL EXCEPTION IN SYSTEM PROCESS: [线程名]
  • 当 app 进程crash 提示三个内容:
    • FATAL EXCEPTION: [线程名]
    • Process: [进程名], PID: [pid]
    • 对于processName 为null,只会提示PID。

1.2 setUncaughtExceptionPreHandler

    @SystemApi(client = MODULE_LIBRARIES)
    public static void setUncaughtExceptionPreHandler(
            @Nullable Thread.UncaughtExceptionHandler uncaughtExceptionHandler) {
        Thread.setUncaughtExceptionPreHandler(uncaughtExceptionHandler);
    }

1.3 KillApplicationHandler

//frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
    private static class KillApplicationHandler implements Thread.UncaughtExceptionHandler {
        private final LoggingHandler mLoggingHandler;
        public KillApplicationHandler(LoggingHandler loggingHandler) {
            this.mLoggingHandler = Objects.requireNonNull(loggingHandler);
        }

        @Override
        public void uncaughtException(Thread t, Throwable e) {
            ...
        }
        ...
    }

KillApplicationHandler 类,以及构造中传入的LoggingHandler,都是实现UncaughtExceptionHandler 接口

2 KillApplicationHandler.uncaughtException()

线程因为未捕获的异常停止时,java虚拟机会调用uncaughtException()函数,即调用KillApplicationHandler 中的 uncaughtException() 函数,下面好好看下uncaughtException() 函数

//frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
       public void uncaughtException(Thread t, Throwable e) {
            try {
                //调用LoggingHandler.uncaughtException(),不会反复调用
                ensureLogging(t, e);
 
                //全局变量,用以控制重复进入crash流程,第一次进入后会将该变量置true
                if (mCrashing) return;
                mCrashing = true;
 
                //尝试去停止profiling,因为后面需要kill 进程,内存buffer会丢失,
                //所以尝试停止,来 flush 内存buffer
                if (ActivityThread.currentActivityThread() != null) {
                    ActivityThread.currentActivityThread().stopProfiling();
                }
                //弹出crash对话框,等待处理完成
                ActivityManager.getService().handleApplicationCrash(
                        mApplicationObject, new ApplicationErrorReport.ParcelableCrashInfo(e));
            } catch (Throwable t2) {
                if (t2 instanceof DeadObjectException) {
                    // System process is dead; ignore
                } else {
                    try {
                        Clog_e(TAG, "Error reporting crash", t2);
                    } catch (Throwable t3) {
                        // Even Clog_e() fails!  Oh well.
                    }
                }
            }  finally {
                //确保当前进程彻底杀掉
                Process.killProcess(Process.myPid());
                System.exit(10);
            }
        }

通过 AMS 调用 handleApplicationCrash() 函数进行 crash report,共两个参数

  • 第一个参数为进程对象
  • 第二个参数为ParcelableCrashInfo(父类为 CrashInfo , 实现 Parcelable接口)
  • CrashInfo 类主要是保存 crash 信息:文件名、类名、方法名、对应行号、异常信息等

3 AMS.handleApplicationCrash()

//frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
    public void handleApplicationCrash(IBinder app,
            ApplicationErrorReport.ParcelableCrashInfo crashInfo) {
        ProcessRecord r = findAppProcess(app, "Crash");
        final String processName = app == null ? "system_server"
                : (r == null ? "unknown" : r.processName);
        handleApplicationCrashInner("crash", r, processName, crashInfo);
    }

该函数主要两个操作:

  • 确定进程名;
  • handleApplicationCrashInner() 函数调用;

对于进程名,

  • 当参数 app 为null,表示 system_server 进程;
  • 当参数 app不为null,通过findAppProcess() 确认ProcessRecord,进而确认进程名;

3.1 AMS.handleApplicationCrashInner()

//frameworks/base/services/core/java/com/android/server/am/AMS.java
    void handleApplicationCrashInner(String eventType, ProcessRecord r, String processName,
            ApplicationErrorReport.CrashInfo crashInfo) {
 
        EventLogTags.writeAmCrash(Binder.getCallingPid(),
                UserHandle.getUserId(Binder.getCallingUid()), processName,
                r == null ? -1 : r.info.flags,
                crashInfo.exceptionClassName,
                crashInfo.exceptionMessage,
                crashInfo.throwFileName,
                crashInfo.throwLineNumber);
 
        FrameworkStatsLog.write(FrameworkStatsLog.APP_CRASH_OCCURRED,
                Binder.getCallingUid(),
                eventType,
                processName,
                Binder.getCallingPid(),
                (r != null && r.info != null) ? r.info.packageName : "",
                (r != null && r.info != null) ? (r.info.isInstantApp()
                        ? FrameworkStatsLog.APP_CRASH_OCCURRED__IS_INSTANT_APP__TRUE
                        : FrameworkStatsLog.APP_CRASH_OCCURRED__IS_INSTANT_APP__FALSE)
                        : FrameworkStatsLog.APP_CRASH_OCCURRED__IS_INSTANT_APP__UNAVAILABLE,
                r != null ? (r.isInterestingToUserLocked()
                        ? FrameworkStatsLog.APP_CRASH_OCCURRED__FOREGROUND_STATE__FOREGROUND
                        : FrameworkStatsLog.APP_CRASH_OCCURRED__FOREGROUND_STATE__BACKGROUND)
                        : FrameworkStatsLog.APP_CRASH_OCCURRED__FOREGROUND_STATE__UNKNOWN,
                processName.equals("system_server") ? ServerProtoEnums.SYSTEM_SERVER
                        : (r != null) ? r.getProcessClassEnum()
                                      : ServerProtoEnums.ERROR_SOURCE_UNKNOWN
        );
 
        final int relaunchReason = r == null ? RELAUNCH_REASON_NONE
                        : r.getWindowProcessController().computeRelaunchReason();
        final String relaunchReasonString = relaunchReasonToString(relaunchReason);
        if (crashInfo.crashTag == null) {
            crashInfo.crashTag = relaunchReasonString;
        } else {
            crashInfo.crashTag = crashInfo.crashTag + " " + relaunchReasonString;
        }
 
        addErrorToDropBox(
                eventType, r, processName, null, null, null, null, null, null, crashInfo);
 
        mAppErrors.crashApplication(r, crashInfo);
    }

函数比较长,主要做了下面几件事情

  • 写event log 类似:

    12-01 16:45:29.663198 1260 3220 I am_crash: [21597,0,com.qualcomm.qti.PresenceApp,550026821,java.lang.NoSuchMethodException,com.qualcomm.qti.PresenceApp.SubsriptionTab.<init> [],Class.java,2363]

  • addErrorToDropBox() 将crash 的信息输出到 /data/system/dropbox/ 下,例如system_server 的dropbox 文件名为 system_server_crash@xxx.txt (xxx 代表时间戳);

  • crashApplication() 继续处理 crash 流程,发出 SHOW_ERROR_UI_MSG,弹出 crash 对话框;

4. AppErrors.crashApplication()

//frameworks/base/services/core/java/com/android/server/am/AppErrors.java
    void crashApplication(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo) {
        final int callingPid = Binder.getCallingPid();
        final int callingUid = Binder.getCallingUid();
 
        final long origId = Binder.clearCallingIdentity();
        try {
            crashApplicationInner(r, crashInfo, callingPid, callingUid);
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }
//-------------------------------------------------------------------------------------------
    private void crashApplicationInner(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo,
            int callingPid, int callingUid) {
.........................................................
        AppErrorResult result = new AppErrorResult();
        int taskId;
        synchronized (mService) {
            /**
             * If crash is handled by instance of {@link android.app.IActivityController},
             * finish now and don't show the app error dialog.
             */
            if (handleAppCrashInActivityController(r, crashInfo, shortMsg, longMsg, stackTrace,
                    timeMillis, callingPid, callingUid)) {
                return;
            }

            

            // If we can't identify the process or it's already exceeded its crash quota,
            // quit right away without showing a crash dialog.
            if (r == null || !makeAppCrashingLocked(r, shortMsg, longMsg, stackTrace, data)) {
                return;
            }

            final Message msg = Message.obtain();
            msg.what = ActivityManagerService.SHOW_ERROR_UI_MSG;

            taskId = data.taskId;
            msg.obj = data;
            mService.mUiHandler.sendMessage(msg);
        }

        int res = result.get();

        Intent appErrorIntent = null;
        MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_CRASH, res);
        if (res == AppErrorDialog.TIMEOUT || res == AppErrorDialog.CANCEL) {
            res = AppErrorDialog.FORCE_QUIT;
        }
        switch (res) {
            case AppErrorDialog.MUTE:
                synchronized (mBadProcessLock) {
                    stopReportingCrashesLBp(r);
                }
                break;
            case AppErrorDialog.RESTART:
                synchronized (mService) {
                    mService.mProcessList.removeProcessLocked(r, false, true,
                            ApplicationExitInfo.REASON_CRASH, "crash");
                }
                if (taskId != INVALID_TASK_ID) {
                    try {
                        mService.startActivityFromRecents(taskId,
                                ActivityOptions.makeBasic().toBundle());
                    } catch (IllegalArgumentException e) {
                        // Hmm...that didn't work. Task should either be in recents or associated
                        // with a stack.
                        Slog.e(TAG, "Could not restart taskId=" + taskId, e);
                    }
                }
                break;
            case AppErrorDialog.FORCE_QUIT:
                final long orig = Binder.clearCallingIdentity();
                try {
                    // Kill it with fire!
                    mService.mAtmInternal.onHandleAppCrash(r.getWindowProcessController());
                    if (!r.isPersistent()) {
                        synchronized (mService) {
                            mService.mProcessList.removeProcessLocked(r, false, false,
                                    ApplicationExitInfo.REASON_CRASH, "crash");
                        }
                        mService.mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
                    }
                } finally {
                    Binder.restoreCallingIdentity(orig);
                }
                break;
            case AppErrorDialog.APP_INFO:
                appErrorIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                appErrorIntent.setData(Uri.parse("package:" + r.info.packageName));
                appErrorIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                break;
            case AppErrorDialog.FORCE_QUIT_AND_REPORT:
                synchronized (mProcLock) {
                    appErrorIntent = createAppErrorIntentLOSP(r, timeMillis, crashInfo);
                }
                break;
        }

        if (appErrorIntent != null) {
            try {
                mContext.startActivityAsUser(appErrorIntent, new UserHandle(r.userId));
            } catch (ActivityNotFoundException e) {
                Slog.w(TAG, "bug report receiver dissappeared", e);
            }
        }
    }
  • handleAppCrashInActivityController() 如果是 IActivityController 类型该处理的 crash,是不会弹出对话框,通过该函数进入 makeAppCrashingLocked() 流程。
  • makeAppCrashingLocked() 如果无法识别进程,或者进程已经超过crash 额度,将不再弹出对话框,而是直接return到上一级
  • 发出 SHOW_ERROR_UI_MSG 消息,弹出crash 对话框,详细看下面第 6 节;
  • 等待用户选择,根据不同选择做进一步处理

5 makeAppCrashingLocked

//frameworks/base/services/core/java/com/android/server/am/AppErrors.java
    @GuardedBy("mService")
    private boolean makeAppCrashingLocked(ProcessRecord app,
            String shortMsg, String longMsg, String stackTrace, AppErrorDialog.Data data) {
        synchronized (mProcLock) {
            final ProcessErrorStateRecord errState = app.mErrorState;
            errState.setCrashing(true);
            //封装crash信息到crashingReport对象   generateProcessError  5.1
            errState.setCrashingReport(generateProcessError(app,
                    ActivityManager.ProcessErrorStateInfo.CRASHED,
                    null, shortMsg, longMsg, stackTrace));
            //5.2
            errState.startAppProblemLSP();
            app.getWindowProcessController().stopFreezingActivities();
            synchronized (mBadProcessLock) {
                //5.3
                return handleAppCrashLSPB(app, "force-crash" /*reason*/, shortMsg, longMsg,
                        stackTrace, data);
            }
        }
    }

5.1 generateProcessError()

//frameworks/base/services/core/java/com/android/server/am/AppErrors.java
//通过该函数创建 ActivityManager.ProcessErrorStateInfo 对象,并保存在 app.crashingReport 对象中
    ActivityManager.ProcessErrorStateInfo generateProcessError(ProcessRecord app,
            int condition, String activity, String shortMsg, String longMsg, String stackTrace) {
        ActivityManager.ProcessErrorStateInfo report = new ActivityManager.ProcessErrorStateInfo();
        report.condition = condition;
        report.processName = app.processName;
        report.pid = app.pid;
        report.uid = app.info.uid;
        report.tag = activity;
        report.shortMsg = shortMsg;
        report.longMsg = longMsg;
        report.stackTrace = stackTrace;
        return report;
    }

5.2 startAppProblemLSP()

//frameworks/base/services/core/java/com/android/server/am/ProcessRecord.java
    @GuardedBy({"mService", "mProcLock"})
    void startAppProblemLSP() {
        // 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.
        mErrorReportReceiver = null;

        for (int userId : mService.mUserController.getCurrentProfileIds()) {
            if (mApp.userId == userId) {
                mErrorReportReceiver = ApplicationErrorReport.getErrorReportReceiver(
                        mService.mContext, mApp.info.packageName, mApp.info.flags);
            }
        }
        mService.skipCurrentReceiverLocked(mApp);
    }
  • 当crash 的app 是当前user 下,会通过 getErrorReportReceiver() 获取应用的report receiver,该receiver 指定的Intent 的action 为:android.intent.action.APP_ERROR。
  • 如果没有找到则使用prop ro.error.receiver.default 指定的,否则返回null。

5.2.1 getErrorReportReceiver()

//frameworks/base/core/java/android/app/ApplicationErrorReport.java
    public static ComponentName getErrorReportReceiver(Context context,
            String packageName, int appFlags) {
        //首先global表中需要enable send_action_app_error属性,否则return null
        int enabled = Settings.Global.getInt(context.getContentResolver(),
                Settings.Global.SEND_ACTION_APP_ERROR, 0);
        if (enabled == 0) {
            return null;
        }
 
        PackageManager pm = context.getPackageManager();
 
        // look for receiver in the installer package
        String candidate = null;
        ComponentName result = null;
 
        try {
            //获取该crash应用的包名
            candidate = pm.getInstallerPackageName(packageName);
        } catch (IllegalArgumentException e) {
            // the package could already removed
        }
 
        //第一次获取,使用的是crash应用的包名
        if (candidate != null) {
            result = getErrorReportReceiver(pm, packageName, candidate);
            if (result != null) {
                return result;
            }
        }
 
        //第二次获取,使用prop ro.error.receiver.system.apps指定的包名
        if ((appFlags&ApplicationInfo.FLAG_SYSTEM) != 0) {
            candidate = SystemProperties.get(SYSTEM_APPS_ERROR_RECEIVER_PROPERTY);
            result = getErrorReportReceiver(pm, packageName, candidate);
            if (result != null) {
                return result;
            }
        }
 
        //第三次获取,使用prop ro.error.receiver.default指定的包名
        candidate = SystemProperties.get(DEFAULT_ERROR_RECEIVER_PROPERTY);
        return getErrorReportReceiver(pm, packageName, candidate);
    }

上面最终会调用 getErrorReportReceiver() 的另一个函数,最后的参数为最终的候选包名:

//frameworks/base/core/java/android/app/ApplicationErrorReport.java
   static ComponentName getErrorReportReceiver(PackageManager pm, String errorPackage,
            String receiverPackage) {
        //候选的包名为null 或为空,返回null
        if (receiverPackage == null || receiverPackage.length() == 0) {
            return null;
        }
 
        // 如果与crash的应用包名相同
        if (receiverPackage.equals(errorPackage)) {
            return null;
        }
 
        //action 为android.intent.action.APP_ERROR
        Intent intent = new Intent(Intent.ACTION_APP_ERROR);
        intent.setPackage(receiverPackage);
        ResolveInfo info = pm.resolveActivity(intent, 0);
        if (info == null || info.activityInfo == null) {
            return null;
        }
        return new ComponentName(receiverPackage, info.activityInfo.name);
    }

需要注意的是寻找这个report receiver 是为了在此处makeAppCrashingLocked() 函数返回,crash 对话框弹出之后,根据用户的选择做进一步的处理,包括了通过该 receiver 发从 report 信息:

5.2.2 AMS.skipCurrentReceiverLocked()

    void skipCurrentReceiverLocked(ProcessRecord app) {
        for (BroadcastQueue queue : mBroadcastQueues) {
            queue.skipCurrentReceiverLocked(app);
        }
    }
//frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java
    public void skipCurrentReceiverLocked(ProcessRecord app) {
        BroadcastRecord r = null;
        final BroadcastRecord curActive = mDispatcher.getActiveBroadcastLocked();
        if (curActive != null && curActive.curApp == app) {
            // confirmed: the current active broadcast is to the given app
            r = curActive;
        }
 
        // If the current active broadcast isn't this BUT we're waiting for
        // mPendingBroadcast to spin up the target app, that's what we use.
        if (r == null && mPendingBroadcast != null && mPendingBroadcast.curApp == app) {
            if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
                    "[" + mQueueName + "] skip & discard pending app " + r);
            r = mPendingBroadcast;
        }
 
        if (r != null) {
            skipReceiverLocked(r);
        }
    }
 
    private void skipReceiverLocked(BroadcastRecord r) {
        logBroadcastReceiverDiscardLocked(r);
        finishReceiverLocked(r, r.resultCode, r.resultData,
                r.resultExtras, r.resultAbort, false);
        scheduleBroadcastsLocked();
    }

主要是结束 app进程的广播

5.3 stopFreezingActivities()

//frameworks/base/services/core/java/com/android/server/am/WindowProcessController.java
    public void stopFreezingActivities() {
        synchronized (mAtm.mGlobalLock) {
            int i = mActivities.size();
            while (i > 0) {
                i--;
                mActivities.get(i).stopFreezingScreenLocked(true);
            }
        }
    }

//其中的 mActivities 为 ArrayList<ActivityRecord>,停止进程里所有activity。
//frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java
    void stopFreezingScreenLocked(boolean force) {
        if (force || frozenBeforeDestroy) {
            frozenBeforeDestroy = false;
            if (getParent() == null) {
                return;
            }
            ProtoLog.v(WM_DEBUG_ORIENTATION,
                        "Clear freezing of %s: visible=%b freezing=%b", appToken,
                                isVisible(), isFreezingScreen());
            stopFreezingScreen(true, force);
        }
    }

最终调用的是 WSM.stopFreezingDisplayLocked() 函数,详细可以查看源码,大致流程:

  • 将冻屏相关的信息remove 掉;
  • 屏幕旋转动画的相关操作;
  • display 冻结时,执行GC 操作;
  • 更新当前的屏幕方向;

5.4 handleAppCrashLocked()

这个crash 后续操作流程的核心处理函数:

//frameworks/base/services/core/java/com/android/server/am/AppErrors.java
    @GuardedBy({"mService", "mProcLock", "mBadProcessLock"})
    private boolean handleAppCrashLSPB(ProcessRecord app, String reason,
            String shortMsg, String longMsg, String stackTrace, AppErrorDialog.Data data) {
        final long now = SystemClock.uptimeMillis();
        final boolean showBackground = Settings.Secure.getIntForUser(mContext.getContentResolver(),
                Settings.Secure.ANR_SHOW_BACKGROUND, 0,
                mService.mUserController.getCurrentUserId()) != 0;

        Long crashTime;
        Long crashTimePersistent;
        final String processName = app.processName;
        final int uid = app.uid;
        final int userId = app.userId;
        final boolean isolated = app.isolated;
        final boolean persistent = app.isPersistent();
        final WindowProcessController proc = app.getWindowProcessController();
        final ProcessErrorStateRecord errState = app.mErrorState;

        if (!app.isolated) {
            crashTime = mProcessCrashTimes.get(processName, uid);
            crashTimePersistent = mProcessCrashTimesPersistent.get(processName, uid);
        } else {
            crashTime = crashTimePersistent = null;
        }

        // Bump up the crash count of any services currently running in the proc.
        boolean tryAgain = app.mServices.incServiceCrashCountLocked(now);

        final boolean quickCrash = crashTime != null
                && now < crashTime + ActivityManagerConstants.MIN_CRASH_INTERVAL;
        if (quickCrash || isProcOverCrashLimitLBp(app, now)) {
            // The process either crashed again very quickly or has been crashing periodically in
            // the last few hours. If it was a bound foreground service, let's try to restart again
            // in a while, otherwise the process loses!
            Slog.w(TAG, "Process " + processName + " has crashed too many times, killing!"
                    + " Reason: " + (quickCrash ? "crashed quickly" : "over process crash limit"));
            EventLog.writeEvent(EventLogTags.AM_PROCESS_CRASHED_TOO_MUCH,
                    userId, processName, uid);
            mService.mAtmInternal.onHandleAppCrash(proc);
            if (!persistent) {
                // We don't want to start this process again until the user
                // explicitly does so...  but for persistent process, we really
                // need to keep it running.  If a persistent process is actually
                // repeatedly crashing, then badness for everyone.
                EventLog.writeEvent(EventLogTags.AM_PROC_BAD, userId, uid,
                        processName);
                if (!isolated) {
                    // XXX We don't have a way to mark isolated processes
                    // as bad, since they don't have a persistent identity.
                    markBadProcess(processName, app.uid,
                            new BadProcessInfo(now, shortMsg, longMsg, stackTrace));
                    mProcessCrashTimes.remove(processName, app.uid);
                    mProcessCrashCounts.remove(processName, app.uid);
                }
                errState.setBad(true);
                app.setRemoved(true);
                final AppStandbyInternal appStandbyInternal =
                        LocalServices.getService(AppStandbyInternal.class);
                if (appStandbyInternal != null) {
                    appStandbyInternal.restrictApp(
                            // Sometimes the processName is the same as the package name, so use
                            // that if we don't have the ApplicationInfo object.
                            // AppStandbyController will just return if it can't find the app.
                            app.info != null ? app.info.packageName : processName,
                            userId, UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY);
                }
                // Don't let services in this process be restarted and potentially
                // annoy the user repeatedly.  Unless it is persistent, since those
                // processes run critical code.
                mService.mProcessList.removeProcessLocked(app, false, tryAgain,
                        ApplicationExitInfo.REASON_CRASH, "crash");
                mService.mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
                if (!showBackground) {
                    return false;
                }
            }
            mService.mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
        } else {
            final int affectedTaskId = mService.mAtmInternal.finishTopCrashedActivities(
                            proc, reason);
            if (data != null) {
                data.taskId = affectedTaskId;
            }
            if (data != null && crashTimePersistent != null
                    && now < crashTimePersistent + ActivityManagerConstants.MIN_CRASH_INTERVAL) {
                data.repeating = true;
            }
        }

        if (data != null && tryAgain) {
            data.isRestartableForService = true;
        }

        // If the crashing process is what we consider to be the "home process" and it has been
        // replaced by a third-party app, clear the package preferred activities from packages
        // with a home activity running in the process to prevent a repeatedly crashing app
        // from blocking the user to manually clear the list.
        if (proc.isHomeProcess() && proc.hasActivities() && (app.info.flags & FLAG_SYSTEM) == 0) {
            proc.clearPackagePreferredForHomeActivities();
        }

        if (!isolated) {
            // XXX Can't keep track of crash times for isolated processes,
            // because they don't have a persistent identity.
            mProcessCrashTimes.put(processName, uid, now);
            mProcessCrashTimesPersistent.put(processName, uid, now);
            updateProcessCrashCountLBp(processName, uid, now);
        }

        if (errState.getCrashHandler() != null) {
            mService.mHandler.post(errState.getCrashHandler());
        }
        return true;
    }

6. SHOW_ERROR_UI_MSG 消息

makeAppCrashingLocked() 返回true 时,会通过 AMS.mUiHandler发送 SHOW_ERROR_UI_MSG 消息:

//frameworks/base/services/core/java/com/android/server/am/AMS.java
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case SHOW_ERROR_UI_MSG: {
                    mAppErrors.handleShowAppErrorUi(msg);
                    ensureBootCompleted();
                } break;

最终是调用 mAppErrors.handleShowAppErrorUi(),代码逻辑不是很复杂,这里暂时不做剖析。

正常情况在发生 crash 时,默认系统会弹出提示 crash 的对话框,并阻塞等待用户的选择。

7. killProcess()

crash 的处理流程大致剖析完成,回到第 2 节 KillApplicationHandler.uncaughtException() 最后的finally:

frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
 
       public void uncaughtException(Thread t, Throwable e) {
            try {
                ...
            } catch (Throwable t2) {
                ...
            } finally {
                //确保当前进程彻底杀掉
                Process.killProcess(Process.myPid());
                System.exit(10);
            }
        }

8. 避开uncaught exception

java 端提供了一个接口:

//libcore/ojluni/src/main/java/java/lang/Thread.java
    private volatile UncaughtExceptionHandler uncaughtExceptionHandler;
 
    public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh) {
        checkAccess();
        uncaughtExceptionHandler = eh;
    }

在app 出现 crash 的时候,需要确定当线程中是否设置了 uncaughtExceptionHandler,那么原来的 defaultUncaughtExceptionHandler 将不会被调用,即如果设置了 uncaughtExceptionHandler,最终调用的是 uncaughtExceptionHandler.uncaughtException()。

利用这种方式可以避开 uncaught exception 而引起的 app 被kill。例如:

        if (true) {
            //create thread
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    String str = null;
                    System.out.println(str.length());
                }
            });
            thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
                @Override
                public void uncaughtException(@NonNull Thread t, @NonNull Throwable e) {
                    
                }
            });
            thread.start();
            return;
        }

上面是个简单的示例,只要通过 setUncaughtExceptionHandler() 设置一个新的UncaughtExceptionHandler, 就可以避开上述问题。

9 总结

9.1 调用栈

  1. KillApplicationHandler.uncaughtException()
    1. stopProfiling
    2. AMS.handleApplicationCrash()
      1. handleApplicationCrashInner
    3. AppErrors.crashApplication()
    4. makeAppCrashingLocked
      1. generateProcessError()
      2. startAppProblemLSP()
        1. getErrorReportReceiver
        2. AMS.skipCurrentReceiverLocked()
      3. stopFreezingActivities()
      4. handleAppCrashLocked

9.2 handleAppCrashLocked

当同一进程 1 分钟之内连续两次 crash,则执行:

  • mService.mAtmInternal.onHandleAppCrash()
  • 对于非 persistent 进程:
    • ·mService.mProcessList.removeProcessLocked()
    • mService.mAtmInternal.resumeTopActivities()
  • 对于 persistent 进程:
    • mService.mAtmInternal.resumeTopActivities()
  • 没有在1分钟频繁 crash,则执行
    • mService.mAtmInternal.finishTopCrashedActivities()
  • 20
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值