Android DropBoxManagerサービスの紹介

242098 ワード

DropBox Managerとは?
Enqueues chunks of data (from various sources – application crashes, kernel log records, etc.). The queue is size bounded and will drop old data if the enqueued data exceeds the maximum size. You can think of this as a persistent, system-wide, blob-oriented “logcat”.
DropBoxManagerは、AndroidがFroyo(API level 8)で導入するシステムデータを持続的に記憶するためのメカニズムであり、主にAndroidの実行中、カーネル、システムプロセス、ユーザプロセスなどに重大な問題が発生した場合のlogを記録するためのものであり、持続可能なメモリのシステムレベルのlogcatと考えられる.
パラメータDROPBOXでSERVICEはgetSystemService(String)を呼び出してこのサービスを取得し、DropBox Managerに格納されているすべてのシステムエラーレコードを検索する.
Androidのデフォルトで記録できるシステムエラーはどれですか?
どのシステムエラーがDropBox Managerに記録されるかについてのドキュメントは公式サイトでは見つかりませんが、ソースコードを確認して情報を見つけることができます.ソースコードからDropBox Managerに記録する各種tag(logcatのtagに類似)が検索できる.
crash(アプリケーション強制クローズ、Force Close)
Javaレイヤがcatchにない例外に遭遇すると、ActivityManagerServiceはcrashをDropBoxManagerに記録し、Force Closeダイアログボックスをポップアップしてユーザに提示する.
ActivityManagerService link
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public void handleApplicationCrash(IBinder app, ApplicationErrorReport.CrashInfo crashInfo) {  ProcessRecord r = findAppProcess(app, "Crash");  final String processName = app == null ? "system_server"  : (r == null ? "unknown" : r.processName);   EventLog.writeEvent(EventLogTags.AM_CRASH, Binder.getCallingPid(),  UserHandle.getUserId(Binder.getCallingUid()), processName,  r == null ? -1 : r.info.flags,  crashInfo.exceptionClassName,  crashInfo.exceptionMessage,  crashInfo.throwFileName,  crashInfo.throwLineNumber);   addErrorToDropBox("crash", r, processName, null, null, null, null, null, crashInfo);   crashApplication(r, crashInfo); } 

anr(アプリケーション応答なし、Application Not Responding,ANR)
アプリケーションのメインスレッド(UIスレッド)が長時間応答することができない場合、ActivityManagerServiceはanrをDropBoxManagerに記録し、Application Not Respondingダイアログボックスをポップアップしてユーザに提示する.
ActivityManagerService link
1
2
3
4
5
6
7
final void appNotResponding(ProcessRecord app, ActivityRecord activity,  ActivityRecord parent, boolean aboveSystem, final String annotation) {  //......  addErrorToDropBox("anr", app, app.processName, activity, parent, annotation,  cpuInfo, tracesFile, null);  //...... } 

wtf (What a Terrible Failure)
‘android.util.Log’クラスは静的なwtf関数を提供し、アプリケーションはコードの中で発生すべきでない状況を自発的に報告することができる.システム設定に依存する、この関数はActivity Management Servicesを介してDropBox Managerに記録するwtfを追加し、現在のアプリケーションプロセスを終了する.
ActivityManagerService link
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public boolean handleApplicationWtf(IBinder app, String tag,  ApplicationErrorReport.CrashInfo crashInfo) {  ProcessRecord r = findAppProcess(app, "WTF");  final String processName = app == null ? "system_server"  : (r == null ? "unknown" : r.processName);   EventLog.writeEvent(EventLogTags.AM_WTF,  UserHandle.getUserId(Binder.getCallingUid()), Binder.getCallingPid(),  processName,  r == null ? -1 : r.info.flags,  tag, crashInfo.exceptionMessage);   addErrorToDropBox("wtf", r, processName, null, null, tag, null, null, crashInfo);   if (r != null && r.pid != Process.myPid() &&  Settings.Global.getInt(mContext.getContentResolver(),  Settings.Global.WTF_IS_FATAL, 0) != 0) {  crashApplication(r, crashInfo);  return true;  } else {  return false;  } } 

strict_mode (StrictMode Violation)
StrictMode(厳格モード)は、その名の通り、通常モードよりも厳格に検出する、通常はメインスレッドで実行すべきでないネットワーク、ファイルなどの動作を監視するために用いられる.任意のStrictModeの違反は、Activity Management ServicesによってDropBoxManagerにstrict_modeの違反として記録される.
ActivityManagerService link
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public void handleApplicationStrictModeViolation(  IBinder app,  int violationMask,  StrictMode.ViolationInfo info) {  ProcessRecord r = findAppProcess(app, "StrictMode");  if (r == null) {  return;  }   if ((violationMask & StrictMode.PENALTY_DROPBOX) != 0) {  Integer stackFingerprint = info.hashCode();  boolean logIt = true;  synchronized (mAlreadyLoggedViolatedStacks) {  if (mAlreadyLoggedViolatedStacks.contains(stackFingerprint)) {  logIt = false;  // TODO: sub-sample into EventLog for these, with  // the info.durationMillis? Then we'd get  // the relative pain numbers, without logging all  // the stack traces repeatedly. We'd want to do  // likewise in the client code, which also does  // dup suppression, before the Binder call.  } else {  if (mAlreadyLoggedViolatedStacks.size() >= MAX_DUP_SUPPRESSED_STACKS) {  mAlreadyLoggedViolatedStacks.clear();  }  mAlreadyLoggedViolatedStacks.add(stackFingerprint);  }  }  if (logIt) {  logStrictModeViolationToDropBox(r, info);  }  }  //...... } 

lowmem(低メモリ)
メモリが不足するとAndroidはバックグラウンドアプリケーションを終了してメモリを解放するが、バックグラウンドアプリケーションが解放されない場合、ActivityManagerServiceはDropBoxManagerにlowmemを1回記録する.
ActivityManagerService link
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 public void handleMessage(Message msg) {  switch (msg.what) {  //...  case REPORT_MEM_USAGE: {  //......  Thread thread = new Thread() {  @Override public void run() {  StringBuilder dropBuilder = new StringBuilder(1024);  StringBuilder logBuilder = new StringBuilder(1024);  //......  addErrorToDropBox("lowmem", null, "system_server", null,  null, tag.toString(), dropBuilder.toString(), null, null);  //......  }  };  thread.start();  break;  }  //......  } 

watchdog
WatchDogがシステムプロセス(system_server)に問題が発生したことを監視すると、watchdogがDropBox Managerに記録され、システムプロセスの実行が終了する.
Watchdog link
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/** This class calls its monitor every minute. Killing this process if they don't return **/ public class Watchdog extends Thread {  //......  @Override  public void run() {  boolean waitedHalf = false;  while (true) {  //......   // If we got here, that means that the system is most likely hung.  // First collect stack traces from all threads of the system process.  // Then kill this process so that the system will restart.   //......   // Try to add the error to the dropbox, but assuming that the ActivityManager  // itself may be deadlocked. (which has happened, causing this statement to  // deadlock and the watchdog as a whole to be ineffective)  Thread dropboxThread = new Thread("watchdogWriteToDropbox") {  public void run() {  mActivity.addErrorToDropBox(  "watchdog", null, "system_server", null, null,  name, null, stack, null);  }  };  dropboxThread.start();  try {  dropboxThread.join(2000); // wait up to 2 seconds for it to return.  } catch (InterruptedException ignored) {}   //......  }  }   //...... } 

netstats_error
NetworkStatsServiceは、ストレージのネットワーク状態の統計データを収集し、永続化します.明らかなネットワーク状態エラーが発生した場合、DropBoxManagerに記録するnetstats_errorを追加します.
BATTERY_DISCHARGE_INFO
BatteryServiceは充電状態を検出し、携帯電話のバッテリー情報を更新する.明らかなdischargeイベントに遭遇すると、BATTERY_DISCHARGE_INFOがDropBoxManagerに記録されます.
システムサービス起動完了後の検出
システムサービス(System Services)の起動が完了すると、次のような一連のセルフテストが行われます.
電源を入れるたびにSYSTEM_BOOTの記録が増加する.
System Serverの再起動システムサービス(System Server)が起動後の最初の起動でない場合、SYSTEM_RESTARTの記録が追加されます.通常、システムサービス(System Server)は1回の起動で1回しか起動しません.2回目の起動はbugを意味します.
Kernel Panic(カーネルエラー)Kernel Panicが発生すると、Kernelはいくつかのlog情報をファイルシステムに記録します.Kernelはすでに切れているので、もちろんこの時にエラー情報を記録する機会はありません.Kernel Panicを検出できる唯一の方法は、携帯電話の起動後にこれらのlogファイルが存在するかどうかを確認し、存在する場合は前回の携帯電話がKernel Panicのためにダウンタイムしたことを意味し、これらのログをDropBoxManagerに記録することである.DropBoxManagerレコードTAG名と対応するファイル名はそれぞれ:SYSTEM_LAST_KMSG/proc/last_kmsgが存在する場合. APANIC_CONSOLE/data/dontpanic/apanic_consoleが存在する場合. APANIC_THREADS/data/dontpanic/apanic_threadsが存在する場合.

システムリカバリ(System Recovery)は、ファイル/cache/recovery/logが存在するか否かを検出することによって、システムリカバリにより機器が再起動するか否かを検出し、SYSTEM_RECOVERY_LOGを追加してDropBoxManagerに記録する.
System Server BootReceiver link
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
private void logBootEvents(Context ctx) throws IOException {  final DropBoxManager db = (DropBoxManager) ctx.getSystemService(Context.DROPBOX_SERVICE);  final SharedPreferences prefs = ctx.getSharedPreferences("log_files", Context.MODE_PRIVATE);  final String headers = new StringBuilder(512)  .append("Build: ").append(Build.FINGERPRINT).append("
"
)
.append("Hardware: ").append(Build.BOARD).append("
"
)
.append("Revision: ") .append(SystemProperties.get("ro.revision", "")).append("
"
)
.append("Bootloader: ").append(Build.BOOTLOADER).append("
"
)
.append("Radio: ").append(Build.RADIO).append("
"
)
.append("Kernel: ") .append(FileUtils.readTextFile(new File("/proc/version"), 1024, "...
"
))
.append("
"
).toString();
String recovery = RecoverySystem.handleAftermath(); if (recovery != null && db != null) { db.addText("SYSTEM_RECOVERY_LOG", headers + recovery); } if (SystemProperties.getLong("ro.runtime.firstboot", 0) == 0) { String now = Long.toString(System.currentTimeMillis()); SystemProperties.set("ro.runtime.firstboot", now); if (db != null) db.addText("SYSTEM_BOOT", headers); // Negative sizes mean to take the *tail* of the file (see FileUtils.readTextFile()) addFileToDropBox(db, prefs, headers, "/proc/last_kmsg", -LOG_SIZE, "SYSTEM_LAST_KMSG"); addFileToDropBox(db, prefs, headers, "/cache/recovery/log", -LOG_SIZE, "SYSTEM_RECOVERY_LOG"); addFileToDropBox(db, prefs, headers, "/data/dontpanic/apanic_console", -LOG_SIZE, "APANIC_CONSOLE"); addFileToDropBox(db, prefs, headers, "/data/dontpanic/apanic_threads", -LOG_SIZE, "APANIC_THREADS"); } else { if (db != null) db.addText("SYSTEM_RESTART", headers); } // Scan existing tombstones (in case any new ones appeared) File[] tombstoneFiles = TOMBSTONE_DIR.listFiles(); for (int i = 0; tombstoneFiles != null && i < tombstoneFiles.length; i++) { addFileToDropBox(db, prefs, headers, tombstoneFiles[i].getPath(), LOG_SIZE, "SYSTEM_TOMBSTONE"); } // Start watching for new tombstone files; will record them as they occur. // This gets registered with the singleton file observer thread. sTombstoneObserver = new FileObserver(TOMBSTONE_DIR.getPath(), FileObserver.CLOSE_WRITE) { @Override public void onEvent(int event, String path) { try { String filename = new File(TOMBSTONE_DIR, path).getPath(); addFileToDropBox(db, prefs, headers, filename, LOG_SIZE, "SYSTEM_TOMBSTONE"); } catch (IOException e) { Slog.e(TAG, "Can't log tombstone", e); } } }; sTombstoneObserver.startWatching(); }

SYSTEM_TOMBSTONE(Nativeプロセスのクラッシュ)
TombstoneはAndroidがnativeプロセスのクラッシュを記録するためのcore dumpログであり、システムサービスは起動完了後にtombstoneログファイルの変化を検出するためにObserverを追加し、新しいtombstoneファイルを生成するたびにSYSTEM_TOMBSTONEファイルをDropBoxManagerに記録する.
DropBox Managerはどのように記録データを保存しますか?
DropBoxManagerはファイルストレージを使用する、すべてのレコードは/data/system/dropboxディレクトリに格納され、1つのレコードは1つのファイルであり、テキストファイルのサイズがファイルシステムの最小ブロックサイズを超えると、DropBoxManagerは自動的にファイルを圧縮し、通常ファイル名はDropBoxManagerのTAGパラメータの先頭を呼び出す.
System Server BootReceiver link
1
2
3
4
5
6
7
8
9
10
$ adb shell ls -l /data/system/dropbox -rw------- system system 258 2012-11-21 11:36 SYSTEM_RESTART@1353469017940.txt -rw------- system system 39 2012-11-21 11:40 event_data@1353469222884.txt -rw------- system system 39 2012-11-21 12:10 event_data@1353471022975.txt -rw------- system system 34 2012-11-21 18:10 event_log@1353492624170.txt -rw------- system system 34 2012-11-21 18:40 event_log@1353494424296.txt -rw------- system system 34 2012-11-22 10:10 event_log@1353550227432.txt -rw------- system system 1528 2012-11-21 22:54 system_app_crash@1353509648395.txt -rw------- system system 1877 2012-11-21 11:36 system_app_strictmode@1353469014395.txt -rw------- system system 3724 2012-11-21 11:36 system_app_strictmode@1353469014924.txt.gz 

DropBox Managerの利用方法
DropBoxManagerにより永続化が必要なエラーログ情報を記録するDropBoxManagerは、logcat以外の別のエラーログ記録メカニズムを提供し、エラー時に自動的に関連情報をDropBoxManagerに記録することができる.DropBoxManagerはlogcatよりもプログラムの自動ミスに適しており、人為的な要因によるミス漏れを避けることができる.またDropBoxManagerはAndroidシステムの公開サービスである、多くのプライベート実装に比べて互換性の問題が発生する確率が大幅に低下する.
エラー自動エスカレーションは、DropBox ManagerとデバイスのBugReportを組み合わせて、サーバへの自動エスカレーションエラーを実現することができる.新しいレコードを生成するたびに、DropBoxManagerはDropBoxManager.ACTION_DROPBOX_ENTRY_ADDED Intentをブロードキャストし、デバイスのBugReportサービスはこのIntentをリスニングし、エラーの自動報告をトリガーする必要がある.参照先:http://xiaocong.github.com/blog/2012/11/21/to-introduce-android-dropboxmanager-service/