Android System crash DeadSystemException (Service/Activity/ultimate solution)

DeadSystemException

The core Android system has died and is going through a runtime restart. All running apps will be promptly killed. The
core Android system service has died and is going through a runtime restart. All running apps are about to be killed by kill.

Please read more, DeadSystemException official introduction

The solution given by chatgpt :
DeadSystemException in Android is a very serious exception, indicating that the system has crashed. If not handled, the application will crash and exit.

Processing method :

  1. Catch the exception: use try-catch to catch DeadSystemException, or register an UncaughtExceptionHandler to handle uncaught exceptions
  2. Perform business logic processing according to product requirements: perform corresponding processing, such as outputting error logs or prompting users to restart the application.

1. The exception occurs in service create:

insert image description here
First look at the place where the exception is thrown in the call stack:
/frameworks/base/core/java/android/app/ActivityThread.javamiddle handleCreateService():
insert image description here
insert image description here
From the source code:

  1. The app process has completed the process of service#oncreate(), and then communicates with ams through the binder, and DeadObjectException occurs and DeadSystemException is thrown.
  2. ActivityThread has an exception handling mechanism try-catch for the service creation process, which will determine whether Instrumentation intercepts the exception.

In order to further understand the reason, then check the logs captured in the exception on bugly and find several valid logs:

//系统服务已经die死亡
AndroidRuntime: FATAL EXCEPTION: main
AndroidRuntime: Process: com.android.systemui, PID: 19210
AndroidRuntime: DeadSystemException: The system died; earlier logs will point to the root cause
AppErrors: Process com.android.systemui has crashed too many times: killing!

//binder 通讯异常抛出日志
JavaBinder: !!! FAILED BINDER TRANSACTION !!! (parcel size = 104)

According to the analysis, it is inferred that the system service has died, and the app process communicates with it through the binder, and DeadSystemException will be thrown.

I look for it by FAILED BINDER TRANSACTIONa search /frameworks/base/core/jni/android_util_Binder.cpp.
signalExceptionForError():
insert image description here
It can be seen from the binder communication source code that when an exception returns status (failed_transaction and dead_object), DeadObjectException will be thrown. In addition, if you read and write more than 200k of binder data at a time, a TransactionTooLargeException will be thrown;

Cause inference : After the app process successfully creates the service, during the communication process with ams, the binder has an abnormal state status failed_transansaction, which causes an exception to be thrown.

Solution:
use the hook method, and the proxy Instrumentation catches the exception:

 /**
     * 自定义 Instrumentation 子类,
     * 拦截对Activity和Service的系统异常拦截
     */
    private static class InstrumentationImpl extends Instrumentation{
    
    
        @Override
        public boolean onException(Object obj, Throwable e) {
    
    
             return isInterruptException(e);
        }
        public static void hookInstrumentation(){
    
    
            //基本上发生在7.0和7.1设备上
            if (Build.VERSION.SDK_INT==Build.VERSION_CODES.N_MR1||Build.VERSION.SDK_INT==Build.VERSION_CODES.N){
    
    
                try {
    
    
                    Instrumentation ins = new InstrumentationImpl();
                    Class cls = Class.forName("android.app.ActivityThread");
                    Method mthd = cls.getDeclaredMethod("currentActivityThread", (Class[]) null);
                    Object currentAT = mthd.invoke(null, (Object[]) null);
                    Field mInstrumentation = currentAT.getClass().getDeclaredField("mInstrumentation");
                    mInstrumentation.setAccessible(true);
                    mInstrumentation.set(currentAT, ins);
                }catch (Exception e){
    
    
                    e.printStackTrace();
                }
            }
        }
    }
}
private static boolean isInterruptException(Throwable e){
    
    
    if (e.toString().contains("DeadSystemException")){
    
    
        //拦截DeadSystemException
        return true;
    }
     return false;
}

The above code is called in Application's oncreate().

There are other hook methods, the handler callBack of ActivityThread#H, which can also achieve the above effects in general;

After catching this exception, the service that needs to be started cannot be started. Bugly will not catch this exception, but the app process is likely to be killed.

2. The exception occurs in activity destroy :

First check the call stack:
insert image description here
check the source code where the crash is located:
insert image description here
it is somewhat different from the previous question. When the app process has completed the Activity destruction process, the ams process is notified that a system process occurs, and a death exception is thrown directly.

Solution :
There is no exception capture mechanism in the process of App destroying the Activity, but the call stack is called in the handler named H in the ActivityThread, so consider hook for exception capture.

Check the source code of Handler again, and find that you can set callBack for it to intercept its handleMessage() call.

insert image description here
Write the code as follows:

private static class  CallBackImpl implements Handler.Callback{
    
    
    private  Handler activityThreadHandler;
    public CallBackImpl(Handler activityThreadHandler) {
    
    
        this.activityThreadHandler = activityThreadHandler;
    }
    @Override
    public boolean handleMessage(Message msg) {
    
    
        try {
    
    
            activityThreadHandler.handleMessage(msg); 
        }catch (Exception e){
    
    
            if (!isInterruptException(e)){
    
    
                //不被拦截的异常,继续抛出
                throw  e; 
            }   
        }
        return true;
    }
    public static void hook(){
    
    
        try {
    
    
            if (Build.VERSION.SDK_INT==Build.VERSION_CODES.N_MR1||Build.VERSION.SDK_INT==Build.VERSION_CODES.N){
    
    
            //获取到ActivityThread
            Class<?> ActivityThreadClass = Class.forName("android.app.ActivityThread");
            Field sCurrentActivityThreadField = ActivityThreadClass.getDeclaredField("sCurrentActivityThread");
            sCurrentActivityThreadField.setAccessible(true);
            Object ActivityThread = sCurrentActivityThreadField.get(null);
            //获取到ActivityThread中的handler
            Field mHField=ActivityThreadClass.getDeclaredField("mH");
            mHField.setAccessible(true);
            Handler mHandler=(Handler) mHField.get(ActivityThread);
            //给handler添加callback监听器,拦截
            Field mCallBackField = Handler.class.getDeclaredField("mCallback");
            mCallBackField.setAccessible(true);
            mCallBackField.set(mHandler, new CallBackImpl(mHandler));
            }
        }catch (Exception e){
    
    
            e.printStackTrace();
        }
    }
}

private static boolean isInterruptException(Throwable e){
    
    
    if (e.toString().contains("DeadSystemException")){
    
    
        //拦截DeadSystemException
        return true;
    }
     return false;
}

The above code is called in Application's oncreate()

3. The exception occurs when activity stop :

insert image description here
View the source code:
insert image description here
StopInfo This task is added when the ActivityThread executes the Activity onstop state:
insert image description here
handleStopActivity() is called in the handler of H in the ActivityThread.

Solution :
Because StopInfo is a runnable interface, it cannot be intercepted by the Handler's handleMessage() function, so consider directly proxying the Handler of H, distribute it through dispatchMessage(), and pass it to H for processing to catch exceptions.

private static class HandlerImpl extends Handler {
    
    
    private Handler proxy;
    public HandlerImpl(Handler proxy) {
    
    
        super(proxy.getLooper());
        this.proxy = proxy;
    }
    @Override
    public void dispatchMessage(@NonNull Message msg) {
    
    
        try {
    
    
            proxy.dispatchMessage(msg);
        } catch (Exception e) {
    
    
            if (!isInterruptException(e)) {
    
    
                //不被拦截的异常,继续抛出
                throw e;
            }
        }
    }
    public static void hook() {
    
    
        try {
    
    
            if (Build.VERSION.SDK_INT == Build.VERSION_CODES.N_MR1 || Build.VERSION.SDK_INT == Build.VERSION_CODES.N) {
    
    
                //获取到ActivityThread
                Class<?> ActivityThreadClass = Class.forName("android.app.ActivityThread");
                Field sCurrentActivityThreadField = ActivityThreadClass.getDeclaredField("sCurrentActivityThread");
                sCurrentActivityThreadField.setAccessible(true);
                Object ActivityThread = sCurrentActivityThreadField.get(null);
                //获取到ActivityThread中的handler
                Field mHField = ActivityThreadClass.getDeclaredField("mH");
                mHField.setAccessible(true);
                Handler mHandler = (Handler) mHField.get(ActivityThread);
                mHField.set(ActivityThread, new HandlerImpl(mHandler));
            }
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
    }
}
private static boolean isInterruptException(Throwable e) {
    
    
    if (e.toString().contains("DeadSystemException")) {
    
    
        //拦截DeadSystemException
        return true;
    }
    return false;
}

Hook H This Handler can solve the exception capture in the Activity or Service life cycle. Called in Application's oncreate().

4. (Ultimate solution, capture the call stack wherever the problem occurs) :

In fact, other threads or other handlers may encounter this problem when calling cross-process-related APIs. Simply hooking the Handler in the ActivityThread cannot solve it.
Ultimate solution: Catch the exception through the exception handler of the main thread, and customize Thread.UncaughtExceptionHandler.

/**
 * 终极解决方案
 */
private static class ExceptionHandlerImpl implements Thread.UncaughtExceptionHandler{
    
    
    private final Thread.UncaughtExceptionHandler mOldHandler;
    public ExceptionHandlerImpl(Thread.UncaughtExceptionHandler mOldHandler) {
    
    
        this.mOldHandler = mOldHandler;
    }

    @Override
    public void uncaughtException(@NonNull Thread t, @NonNull Throwable e) {
    
    
        if (isInterruptException( e)) {
    
    
            // 异常逻辑 1: 继续执行,进程不结束
            resumeMainThreadLoop();
            // restartApp() 或者直接中断该进程,进行重启重启该app 的逻辑2
            return;
        }
        if (mOldHandler != null) {
    
    
            mOldHandler.uncaughtException(t, e);
        }
    }
    private void restartApp(Context context){
    
    
        // 重新启动应用程序或者系统服务
        final Intent intent = context.getPackageManager().getLaunchIntentForPackage(context.getPackageName());
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
        AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        alarmManager.set(AlarmManager.RTC, System.currentTimeMillis() + 1000, pendingIntent);
        // 退出应用程序或者停止服务
        System.exit(0);
    }
    private void resumeMainThreadLoop() {
    
    
        if (Looper.myLooper() != Looper.getMainLooper()) {
    
    
             return;
        }
        try {
    
    
            Looper.loop();
        } catch (Exception e) {
    
    
            uncaughtException(Thread.currentThread(), e);
        }
    }
    public static void init(){
    
    
            //在bugly初始化或者自定义crash上报组件之后调用
            Thread.UncaughtExceptionHandler mOldHandler = Thread.getDefaultUncaughtExceptionHandler();
            if (!(mOldHandler instanceof ExceptionHandlerImpl)) {
    
    
                Thread.setDefaultUncaughtExceptionHandler(new ExceptionHandlerImpl(mOldHandler));
            }
    }
}

The above code is called after the bugly or exception catching component.

Guess you like

Origin blog.csdn.net/hexingen/article/details/130553448
Recommended