[解BUG系列]从一个错误出发看Android WTF

6,160 阅读2分钟

虽然What the Fuck也是简称WTF,但是这里不是这个意思。

What a Terrible Failure简称WTF,WTF是Android系统记录错误的一种方式,报告一个永远不应该发生的情况,有些只是打印Error stack trace和存异常信息文件到dropbox,有些会crash app。

从一个例子开始

下面这例只是打印异常信息,并不会crash app

  • 日志表现形式
03-15 22:12:50.163  3082  6288 E ActivityManager: Sending non-protected broadcast com.example.intent.action.TEST_ON from system 9411:com.example.testapp/1000 pkg com.example.testapp
03-15 22:12:50.163  3082  6288 E ActivityManager: java.lang.Throwable
03-15 22:12:50.163  3082  6288 E ActivityManager:       at com.android.server.am.ActivityManagerService.checkBroadcastFromSystem(ActivityManagerService.java:21349)
03-15 22:12:50.163  3082  6288 E ActivityManager:       at com.android.server.am.ActivityManagerService.broadcastIntentLocked(ActivityManagerService.java:21953)
03-15 22:12:50.163  3082  6288 E ActivityManager:       at com.android.server.am.ActivityManagerService.broadcastIntent(ActivityManagerService.java:22095)
03-15 22:12:50.163  3082  6288 E ActivityManager:       at android.app.IActivityManager$Stub.onTransact$broadcastIntent$(IActivityManager.java:10171)
03-15 22:12:50.163  3082  6288 E ActivityManager:       at android.app.IActivityManager$Stub.onTransact(IActivityManager.java:167)
03-15 22:12:50.163  3082  6288 E ActivityManager:       at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:3295)
03-15 22:12:50.163  3082  6288 E ActivityManager:       at android.os.Binder.execTransact(Binder.java:731)
  • dropbox表现形式
Process: system_server
Subject: ActivityManager
Build: Realtek/RealtekATV/RealtekATV:9/PTT1.190222.001/chenlong07011548:userdebug/test-keys

android.util.Log$TerribleFailure: Sending non-protected broadcast com.example.intent.action.TEST_ON from system 6702:com.example.testapp/1000 pkg com.example.testapp
    at android.util.Log.wtf(Log.java:299)
    at android.util.Log.wtf(Log.java:294)
    at com.android.server.am.ActivityManagerService.checkBroadcastFromSystem(ActivityManagerService.java:21348)
    at com.android.server.am.ActivityManagerService.broadcastIntentLocked(ActivityManagerService.java:21953)
    at com.android.server.am.ActivityManagerService.broadcastIntent(ActivityManagerService.java:22095)
    at android.app.IActivityManager$Stub.onTransact$broadcastIntent$(IActivityManager.java:10171)
    at android.app.IActivityManager$Stub.onTransact(IActivityManager.java:167)
    at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:3295)
    at android.os.Binder.execTransact(Binder.java:731)
Caused by: java.lang.Throwable
    at com.android.server.am.ActivityManagerService.checkBroadcastFromSystem(ActivityManagerService.java:21349)
    ... 6 more

异常抛出的原因:系统进程com.example.testapp发送了未受保护的广播com.example.intent.action.TEST_ON

异常是由AMS检查广播的时候主动抛出的

瞅两眼源代码,看看咋回事

异常是在AMS.checkBroadcastFromSystem()产生的

    private void checkBroadcastFromSystem(Intent intent, ProcessRecord callerApp,
            String callerPackage, int callingUid, boolean isProtectedBroadcast, List receivers) {
        if ((intent.getFlags() & Intent.FLAG_RECEIVER_FROM_SHELL) != 0) {
            // Don't yell about broadcasts sent via shell
            return;
        }

        final String action = intent.getAction();
        if (isProtectedBroadcast
                || Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
                || Intent.ACTION_DISMISS_KEYBOARD_SHORTCUTS.equals(action)
                || Intent.ACTION_MEDIA_BUTTON.equals(action)
                || Intent.ACTION_MEDIA_SCANNER_SCAN_FILE.equals(action)
                || Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS.equals(action)
                || Intent.ACTION_MASTER_CLEAR.equals(action)
                || Intent.ACTION_FACTORY_RESET.equals(action)
                || AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)
                || AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)
                || LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION.equals(action)
                || TelephonyIntents.ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE.equals(action)
                || SuggestionSpan.ACTION_SUGGESTION_PICKED.equals(action)
                || AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION.equals(action)
                || AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION.equals(action)) {
            // Broadcast is either protected, or it's a public action that
            // we've relaxed, so it's fine for system internals to send.
            return;
        }

        if (callerApp != null) {
            Log.wtf(TAG, "Sending non-protected broadcast " + action
                            + " from system " + callerApp.toShortString() + " pkg " + callerPackage,
                    new Throwable());
        } else {
            Log.wtf(TAG, "Sending non-protected broadcast " + action
                            + " from system uid " + UserHandle.formatUid(callingUid)
                            + " pkg " + callerPackage,
                    new Throwable());
        }
    }

Android系统规定:系统(system)进程发出的广播,必须是受保护的广播,除了Intent.ACTION_CLOSE_SYSTEM_DIALOGS、Intent.ACTION_DISMISS_KEYBOARD_SHORTCUTS、Intent.ACTION_FACTORY_RESET等十几个广播(详细请见AMS.checkBroadcastFromSystem())之外。

本例中的广播com.example.intent.action.TEST_ON是应用自己定义,并不是系统已有的受保护广播,然而应用又是system应用,所以系统就通过WTF报了个异常。

Android系统通过Log.wtf()来打印WTF异常。

frameworks/base/core/java/android/util/Log.java

public static int wtf(String tag, String msg, Throwable tr) {
    return wtf(LOG_ID_MAIN, tag, msg, tr, false, false);
}

static int wtf(int logId, String tag, String msg, Throwable tr, boolean localStack,
        boolean system) {
    TerribleFailure what = new TerribleFailure(msg, tr);
    // 输入wtf msg到终端,Log.v()就是调用这句
    int bytes = printlns(logId, ERROR, tag, msg, localStack ? what : tr);
    // WTF 处理机制
    sWtfHandler.onTerribleFailure(tag, what, system);
    return bytes;
}

private static TerribleFailureHandler sWtfHandler = new TerribleFailureHandler() {
        public void onTerribleFailure(String tag, TerribleFailure what, boolean system) {
            // 调用RuntimeInit.wtf()
            RuntimeInit.wtf(tag, what, system);
        }
    };

frameworks/base/core/java/com/android/internal/os/RuntimeInit.java

public static void wtf(String tag, Throwable t, boolean system) {
    try {
        // 调用AMS的方法,由AMS决定要不要crass app
        if (ActivityManager.getService().handleApplicationWtf(
                mApplicationObject, tag, system,
                new ApplicationErrorReport.ParcelableCrashInfo(t))) {
            // The Activity Manager has already written us off -- now exit.
            Process.killProcess(Process.myPid());
            System.exit(10);
        }
    } catch (Throwable t2) {
        if (t2 instanceof DeadObjectException) {
            // System process is dead; ignore
        } else {
            Slog.e(TAG, "Error reporting WTF", t2);
            Slog.e(TAG, "Original WTF:", t);
        }
    }
}

本例的解决方案

把广播com.example.intent.action.TEST_ON添加为系统的受保护广播

frameworks/base/core/res/AndroidManifest.xml

<protected-broadcast android:name="com.example.intent.action.TEST_ON" />