注释
/**
* Implementation of {@link IDropBoxManagerService} using the filesystem.
* Clients use {@link DropBoxManager} to access this service.
*
* DropBoxManager借助IDropBoxManagerService访问DropBoxManagerService
*/
DropBoxManagerService是一个系统服务,用于收集和存储重要的日志或数据,以便在系统崩溃或特定情况下进行问题诊断。
成员变量
private static final String TAG = "DropBoxManagerService";
//文件存久被存储3天时间
private static final int DEFAULT_AGE_SECONDS = 3 * 86400;
//内存不紧张的时候,最多存储1000个文件
private static final int DEFAULT_MAX_FILES = 1000;
//内存紧张的时候,最多存储300个文件
private static final int DEFAULT_MAX_FILES_LOWRAM = 300;
//QUOTA是配额的意思,最大配额大小为10M
private static final int DEFAULT_QUOTA_KB = 10 * 1024;
//QUOTA是配额的意思,dropbox所有文件总大小,配额是总存储空间的10%
private static final int DEFAULT_QUOTA_PERCENT = 10;
//RESERVE是预留的意思,10%
private static final int DEFAULT_RESERVE_PERCENT = 10;
//每5秒检查一次配额使用情况
private static final int QUOTA_RESCAN_MILLIS = 5000;
//是否执行转储操作,默认不执行
private static final boolean PROFILE_DUMP = false;
//写入到protobuf中的单个dropbox entry的最大字节数=256KB
// Max number of bytes of a dropbox entry to write into protobuf.
private static final int PROTO_MAX_DATA_BYTES = 256 * 1024;
// 当单个dropbox entry超过16KB进行压缩
// Size beyond which to force-compress newly added entries.
private static final long COMPRESS_THRESHOLD_BYTES = 16_384;
// TODO: This implementation currently uses one file per entry, which is
// inefficient for smallish entries -- consider using a single queue file
// per tag (or even globally) instead.
// The cached context and derived objects
// 访问内容提供者(Content Providers)的接口
private final ContentResolver mContentResolver;
// Dropbox dir
private final File mDropBoxDir;
// 初始化地时候记录所有被写的日志文件信息
// Accounting of all currently written log files (set in init()).
// 文件的信息的列表
private FileList mAllFiles = null;
// 代表与tag相关联的文件列表
private ArrayMap<String, FileList> mFilesByTag = null;
// 低优先级速度限制周期,默认为0
private long mLowPriorityRateLimitPeriod = 0;
// 低优先级的tags的数组集合
private ArraySet<String> mLowPriorityTags = null;
// Various bits of disk information
// StatFs对象的全局引用
private StatFs mStatFs = null;
//数据以块存储,mBlockSize代表块大小,初始化为0
private int mBlockSize = 0;
//可用的块的数量:是根据系统的空闲空间、用户设定的限制等因素计算得出的
private int mCachedQuotaBlocks = 0; // Space we can use: computed from free space, etc.
// 记录自上次缓存配额更新以来经过的时间(以毫秒为单位)
private long mCachedQuotaUptimeMillis = 0;
// 是否已经被启动,注意volatile关键字,来确保这个状态的变更对所有线程都是立即可见的,
// 并且避免了因指令重排序而导致的潜在问题
private volatile boolean mBooted = false;
//提供一种异步发送广播的方式,以此来避免可能导致的死锁
// Provide a way to perform sendBroadcast asynchronously to avoid deadlocks.
private final DropBoxManagerBroadcastHandler mHandler;
//最大文件数量,-1表示还没有初始化
private int mMaxFiles = -1; // -1 means uninitialized.
mReceiver
/** Receives events that might indicate a need to clean up files. */
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// For ACTION_DEVICE_STORAGE_LOW:
mCachedQuotaUptimeMillis = 0; // Force a re-check of quota size
// Run the initialization in the background (not this main thread).
// The init() and trimToFit() methods are synchronized, so they still
// block other users -- but at least the onReceive() call can finish.
new Thread() {
public void run() {
try {
init();
trimToFit();
} catch (IOException e) {
Slog.e(TAG, "Can't init", e);
}
}
}.start();
}
};
- mReceiver用于接收清理文件相关的广播 ,Action为ACTION_DEVICE_STORAGE_LOW。
- 首先执行了一个操作来强制重新检查配额大小(mCachedQuotaUptimeMillis = 0;)。
- 创建了一个新的线程来在后台执行init()和trimToFit()方法,注意这两个方法都是同步的。
- 目的是接收到指示存储空间低的事件时,在后台线程中重新初始化并清理文件,以确保应用程序不会因存储空间不足而受到影响
mStub
private final IDropBoxManagerService.Stub mStub = new IDropBoxManagerService.Stub() {
@Override
public void addData(String tag, byte[] data, int flags) {
DropBoxManagerService.this.addData(tag, data, flags);
}
@Override
public void addFile(String tag, ParcelFileDescriptor fd, int flags) {
DropBoxManagerService.this.addFile(tag, fd, flags);
}
@Override
public boolean isTagEnabled(String tag) {
return DropBoxManagerService.this.isTagEnabled(tag);
}
@Override
public DropBoxManager.Entry getNextEntry(String tag, long millis, String callingPackage) {
return getNextEntryWithAttribution(tag, millis, callingPackage, null);
}
@Override
public DropBoxManager.Entry getNextEntryWithAttribution(String tag, long millis,
String callingPackage, String callingAttributionTag) {
return DropBoxManagerService.this.getNextEntry(tag, millis, callingPackage,
callingAttributionTag);
}
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
DropBoxManagerService.this.dump(fd, pw, args);
}
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out,
FileDescriptor err, String[] args, ShellCallback callback,
ResultReceiver resultReceiver) {
(new ShellCmd()).exec(this, in, out, err, args, callback, resultReceiver);
}
};
这是一个IDropBoxManagerService.Stub的匿名子类实例。IDropBoxManagerService是一个Binder接口,定义了DropBoxManagerService服务提供的远程方法。Stub类是自动生成的,用于实现Binder通信机制。通过继承Stub类并重写其方法,我们可以定义服务端的实现逻辑。
- addData(String tag, byte[] data, int flags):允许客户端向DropBoxManagerService添加字节数据,这些数据会被标记为指定的tag,并附带一些标志(flags)。
- addFile(String tag, ParcelFileDescriptor fd, int flags):允许客户端向服务添加文件,通过ParcelFileDescriptor提供文件描述符,以便服务可以访问该文件。
- isTagEnabled(String tag):检查指定tag的日志是否已启用。
- getNextEntry(String tag, long millis, String callingPackage)和5. getNextEntryWithAttribution(String tag, long millis, String callingPackage, String callingAttributionTag):这两个方法用于获取指定tag的下一个日志条目。后者还允许指定调用者的归因标签。
- dump(FileDescriptor fd, PrintWriter pw, String[] args):允许服务将其状态或数据输出到给定的文件描述符中,通常用于调试或日志记录。
- onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver resultReceiver):这是一个特殊的方法,允许通过shell命令与DropBoxManagerService交互。它使用FileDescriptor进行输入/输出,并可以接收命令参数和回调。
ShellCmd
private class ShellCmd extends ShellCommand {
@Override
public int onCommand(String cmd) {
if (cmd == null) {
return handleDefaultCommands(cmd);
}
final PrintWriter pw = getOutPrintWriter();
try {
switch (cmd) {
case "set-rate-limit":
final long period = Long.parseLong(getNextArgRequired());
DropBoxManagerService.this.setLowPriorityRateLimit(period);
break;
case "add-low-priority":
final String addedTag = getNextArgRequired();
DropBoxManagerService.this.addLowPriorityTag(addedTag);
break;
case "remove-low-priority":
final String removeTag = getNextArgRequired();
DropBoxManagerService.this.removeLowPriorityTag(removeTag);
break;
case "restore-defaults":
DropBoxManagerService.this.restoreDefaults();
break;
default:
return handleDefaultCommands(cmd);
}
} catch (Exception e) {
pw.println(e);
}
return 0;
}
@Override
public void onHelp() {
PrintWriter pw = getOutPrintWriter();
pw.println("Dropbox manager service commands:");
pw.println(" help");
pw.println(" Print this help text.");
pw.println(" set-rate-limit PERIOD");
pw.println(" Sets low priority broadcast rate limit period to PERIOD ms");
pw.println(" add-low-priority TAG");
pw.println(" Add TAG to dropbox low priority list");
pw.println(" remove-low-priority TAG");
pw.println(" Remove TAG from dropbox low priority list");
pw.println(" restore-defaults");
pw.println(" restore dropbox settings to defaults");
}
}
- ShellCmd类的主要作用是处理与DropBoxManagerService相关的shell命令。在onCommand方法中,它首先检查传入的命令(cmd)是否为null,如果是,则调用handleDefaultCommands(cmd)来处理默认命令。
- 对于非空的cmd,方法通过switch语句来匹配不同的命令,并执行相应的操作:
2.1 set-rate-limit:这个命令用于设置低优先级广播的速率限制周期。它从命令行参数中获取下一个必需参数(预期是一个长整型数值),然后调用DropBoxManagerService.this.setLowPriorityRateLimit(period)来设置这个周期。
2.2 add-low-priority:这个命令用于将指定的标签(TAG)添加到低优先级列表中。它从命令行参数中获取下一个必需参数作为标签,然后调用DropBoxManagerService.this.addLowPriorityTag(addedTag)来添加这个标签。
2.3 remove-low-priority:与add-low-priority相反,这个命令用于从低优先级列表中移除指定的标签。
restore-defaults:这个命令用于恢复DropBoxManagerService的默认设置。
2.4 如果输入的命令与上述任何一个都不匹配,则依然调用handleDefaultCommands(cmd) - onHelp方法用于提供关于这些命令的帮助信息。它打印出每个命令的简短描述和用法,以便用户了解如何与DropBoxManagerService进行交互
- getNextArgRequired()方法是从命令行参数中获取下一个必需参数。
DropBoxManagerBroadcastHandler
/**
* DropBoxManagerBroadcastHandler提供立刻发送广播和延迟发送广播的逻辑
*/
private class DropBoxManagerBroadcastHandler extends Handler {
private final Object mLock = new Object();
//代表发送的消息需要立刻执行
static final int MSG_SEND_BROADCAST = 1;
//代表发送的消息可能要延迟执行
static final int MSG_SEND_DEFERRED_BROADCAST = 2;
//延迟执行的消息,再发送的时候被记录在mDeferredMap中,被执行后,从mDeferredMap中移除
@GuardedBy("mLock")
private final ArrayMap<String, Intent> mDeferredMap = new ArrayMap();
//looper是FgThread消息的looper
DropBoxManagerBroadcastHandler(Looper looper) {
super(looper);
}
/**
* 消息的处理都会执行prepareAndSendBroadcast来发送一个广播,
* 不过被延迟执行的消息会先从mDeferredMap中移除该消息,再执行发送广播
*/
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SEND_BROADCAST:
prepareAndSendBroadcast((Intent) msg.obj);
break;
case MSG_SEND_DEFERRED_BROADCAST:
Intent deferredIntent;
synchronized (mLock) {
deferredIntent = mDeferredMap.remove((String) msg.obj);
}
if (deferredIntent != null) {
prepareAndSendBroadcast(deferredIntent);
}
break;
}
}
/**
* 先校验是否已经开机结束,如果没有intent会添加Intent.FLAG_RECEIVER_REGISTERED_ONL的flag
* 使用sendBroadcastAsUser发送广播,目标是所有拥有READ_LOGS权限的所有用户
*/
private void prepareAndSendBroadcast(Intent intent) {
if (!DropBoxManagerService.this.mBooted) {
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
}
getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
android.Manifest.permission.READ_LOGS);
}
/**
* 通过tag和time创建一个Intent
*/
private Intent createIntent(String tag, long time) {
final Intent dropboxIntent = new Intent(DropBoxManager.ACTION_DROPBOX_ENTRY_ADDED);
dropboxIntent.putExtra(DropBoxManager.EXTRA_TAG, tag);
dropboxIntent.putExtra(DropBoxManager.EXTRA_TIME, time);
return dropboxIntent;
}
/**
* Schedule a dropbox broadcast to be sent asynchronously.
* 立刻发送一个消息,将消息发送到FgThread的消息队列中,并立刻处理
*/
public void sendBroadcast(String tag, long time) {
sendMessage(obtainMessage(MSG_SEND_BROADCAST, createIntent(tag, time)));
}
/**
* Possibly schedule a delayed dropbox broadcast. The broadcast will only be scheduled if
* no broadcast is currently scheduled. Otherwise updated the scheduled broadcast with the
* new intent information, effectively dropping the previous broadcast.
* 发送一个可能延迟执行的消息到FgThread的消息队列,是否延迟执行,取决于mLowPriorityRateLimitPeriod的值
* 每次发送一个消息&#