Android DropboxManagerService源码分析
注释
/*** 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是配额的意思,最大配额大小为10Mprivate 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 dirprivate final File mDropBoxDir;// 初始化地时候记录所有被写的日志文件信息// Accounting of all currently written log files (set in init()).// 文件的信息的列表private FileList mAllFiles = null;// 代表与tag相关联的文件列表private ArrayMap<String, FileList> mFilesByTag = null;// 低优先级速度限制周期,默认为0private long mLowPriorityRateLimitPeriod = 0;// 低优先级的tags的数组集合private ArraySet<String> mLowPriorityTags = null;// Various bits of disk information// StatFs对象的全局引用private StatFs mStatFs = null;//数据以块存储,mBlockSize代表块大小,初始化为0private 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() {@Overridepublic 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() {@Overridepublic void addData(String tag, byte[] data, int flags) {DropBoxManagerService.this.addData(tag, data, flags);}@Overridepublic void addFile(String tag, ParcelFileDescriptor fd, int flags) {DropBoxManagerService.this.addFile(tag, fd, flags);}@Overridepublic boolean isTagEnabled(String tag) {return DropBoxManagerService.this.isTagEnabled(tag);}@Overridepublic DropBoxManager.Entry getNextEntry(String tag, long millis, String callingPackage) {return getNextEntryWithAttribution(tag, millis, callingPackage, null);}@Overridepublic DropBoxManager.Entry getNextEntryWithAttribution(String tag, long millis,String callingPackage, String callingAttributionTag) {return DropBoxManagerService.this.getNextEntry(tag, millis, callingPackage,callingAttributionTag);}@Overridepublic void dump(FileDescriptor fd, PrintWriter pw, String[] args) {DropBoxManagerService.this.dump(fd, pw, args);}@Overridepublic 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 {@Overridepublic 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;}@Overridepublic 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消息的looperDropBoxManagerBroadcastHandler(Looper looper) {super(looper);}/*** 消息的处理都会执行prepareAndSendBroadcast来发送一个广播,* 不过被延迟执行的消息会先从mDeferredMap中移除该消息,再执行发送广播*/@Overridepublic 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的值* 每次发送一个消息&#