Android DropBox 是 Android 用来持续化存储系统数据的一个管理类,主要用于记录 Android 运行过程中, 内核, 系统进程, 用户进程等出现严重问题时的 log, 可以认为这是一个可持续存储的系统级别的 logcat。
本文主要从以下几个方面阐述Android Dropbox。
1.什么是Android Dropbox
Android Dropbox 是 Android 在 Froyo(API level 8) 引入的用来持续化存储系统数据的机制。主要用于记录 Android 运行过程中, 内核, 系统进程, 用户进程等出现严重问题时的 log, 可以认为这是一个可持续存储的系统级别的 logcat。
相关文件记录存储目录:/data/system/dropbox
2.Dropbox源码
主要源码(基于N版本代码:http://androidxref.com/7.0.0_r1/):
/frameworks/base/core/java/android/os/DropBoxManager.java
/frameworks/base/services/core/java/com/android/server/DropBoxManagerService.java
接下来对源码进行初步分析。(备注:因为篇幅原因,源码中...是我省略无关代码)
a.Dropbox的启动流程
作为android系统服务,其启动方式和其他android服务基本一致。
系统启动过程中SystemServer.java中的startOtherServices()方法中启动
private void startOtherServices() {
...
mSystemServiceManager.startService(DropBoxManagerService.class);
...
}
DropBoxManagerService的构造函数:
public DropBoxManagerService(final Context context) {
this(context, new File("/data/system/dropbox"));
}
可以看出dropbox文件存储的目录是:/data/system/dropbox
再就是需要关注DropBoxManagerService中add和dump方法:
private final IDropBoxManagerService.Stub mStub = new IDropBoxManagerService.Stub() {
@Override
public void add(DropBoxManager.Entry entry) {
DropBoxManagerService.this.add(entry);
}
...
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
DropBoxManagerService.this.dump(fd, pw, args);
}
};
DropBoxManager进程通讯时写数据都是通过这个add方法将数据写入到存储目录,通过dump来采集记录数据的。
这里重点提下add方法:
public void add(DropBoxManager.Entry entry) {
...
final Intent dropboxIntent = new Intent(DropBoxManager.ACTION_DROPBOX_ENTRY_ADDED);
dropboxIntent.putExtra(DropBoxManager.EXTRA_TAG, tag);
dropboxIntent.putExtra(DropBoxManager.EXTRA_TIME, time);
if (!mBooted) {
dropboxIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
}
// Call sendBroadcast after returning from this call to avoid deadlock. In particular
// the caller may be holding the WindowManagerService lock but sendBroadcast requires a
// lock in ActivityManagerService. ActivityManagerService has been caught holding that
// very lock while waiting for the WindowManagerService lock.
mHandler.sendMessage(mHandler.obtainMessage(MSG_SEND_BROADCAST, dropboxIntent));
...
}
可以发现:在dropbox在日志存储时会发送系统广播:
DropBoxManager.ACTION_DROPBOX_ENTRY_ADDED
所以我们可以针对这个做一些系统定制,如获取dropbox中的日志以确认相关问题。
另外,DropBoxManagerService中数据dump和add的一些控制数据需了解下:
private static final int DEFAULT_AGE_SECONDS = 3 * 86400;
private static final int DEFAULT_MAX_FILES = 1000;
private static final int DEFAULT_QUOTA_KB = 5 * 1024;
private static final int DEFAULT_QUOTA_PERCENT = 10;
private static final int DEFAULT_RESERVE_PERCENT = 10;
private static final int QUOTA_RESCAN_MILLIS = 5000;
DEFAULT_AGE_SECONDS = 3 * 86400:文件最长可存活时长为3天
DEFAULT_MAX_FILES = 1000:最大dropbox文件个数为1000
DEFAULT_QUOTA_KB = 5 * 1024:分配dropbox空间的最大值5M
DEFAULT_QUOTA_PERCENT = 10:是指dropbox目录最多可占用空间比例10%
DEFAULT_RESERVE_PERCENT = 10:是指dropbox不可使用的存储空间比例10%
QUOTA_RESCAN_MILLIS = 5000:重新扫描retrim时长为5s
上面这些都是默认值,完全可以通过设置content://settings/global数据库对应项来修改。
DropBoxManager是可以供其系统服务直接调用的类。
final DropBoxManager dbox = (DropBoxManager)mContext.getSystemService(Context.DROPBOX_SERVICE);
例如,拿ActivityManagerService中,设备应用出现crash时,报错信息栈就会记录到dropbox目录,下面详细介绍下应用出现crash后日志记录到dropbox的过程。
首先:应用在crash后,ActivityManagerService会处理对应的消息:
HANDLE_APPLICATION_CRASH_TRANSACTION
case HANDLE_APPLICATION_CRASH_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder app = data.readStrongBinder();
ApplicationErrorReport.CrashInfo ci = new ApplicationErrorReport.CrashInfo(data);
handleApplicationCrash(app, ci);
reply.writeNoException();
return true;
}
handleApplicationCrash实现了处理appcrash的流程:
可以看到 addErrorToDropBox方法:
public void addErrorToDropBox(String eventType,
ProcessRecord process, String processName, ActivityRecord activity,
ActivityRecord parent, String subject,
final String report, final File dataFile,
final ApplicationErrorReport.CrashInfo crashInfo) {
...
dbox.addText(dropboxTag, sb.toString());
...
}
代码:dbox.addText(dropboxTag, sb.toString()); 就是调用的DropBoxManager中的addText方法
public void addText(String tag, String data) {
try {
mService.add(new Entry(tag, 0, data));
} catch (RemoteException e) {
if (e instanceof TransactionTooLargeException
&& mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
Log.e(TAG, "App sent too much data, so it was ignored", e);
return;
}
throw e.rethrowFromSystemServer();
}
}
addText实际是调用DropBoxManagerService中add方法最终将crash的trace信息写入dropbox目录。
3.dropbox中记录的文件有哪些呢?
下图是项目中从dropbox目录pull出来的一个目录截图:
可以看到每个文件格式命名都是:processClass _ eventType@时间戳[.txt|txt.gz].
processClass列举:system_server, system_app, data_app;
eventType列举:分为crash anr watchdog wtf strict_mode lowmem
netstats_error Kernel Panic SYSTEM_BOOT SYSTEM_RESTART
BATTERY_DISCHARGE_INFO SYSTEM_TOMBSTONE;
其中@前面部分及dropbox写入的tag。
如列举部分tag含义:
system_app_anr:系统app无响应
system_app_crash:系统app进程崩溃
system_server_native_crash system进程native出现崩溃
system_server_wtf:system进程发生严重错误
system_server_lowmem:system进程内存不足
4.如何利用DropboxManager
a.利用 DropBoxManager 来记录需要相对持久化存储的错误日志信息
优点:自动抓错, 避免人为因素而产生的错误遗漏
b.错误自动上报
可以利用dropbox每生成新的记录, Dropbox 就会发送广播:
DropBoxManager.ACTION_DROPBOX_ENTRY_ADDED
app可以接收监听改广播获取指定的数据文件内容,内容发送到指定的服务器或邮箱完成错误自动上报。
利用前提:app要具有系统权限 因为:DropBoxManager.ACTION_DROPBOX_ENTRY_ADDED是保护性广播,且读取系统日志也需要android.Manifest.permission.READ_LOGS权限