虽然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" />