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是配额的意思,最大配额大小为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();
        }
    };
  1. mReceiver用于接收清理文件相关的广播 ,Action为ACTION_DEVICE_STORAGE_LOW。
  2. 首先执行了一个操作来强制重新检查配额大小(mCachedQuotaUptimeMillis = 0;)。
  3. 创建了一个新的线程来在后台执行init()和trimToFit()方法,注意这两个方法都是同步的。
  4. 目的是接收到指示存储空间低的事件时,在后台线程中重新初始化并清理文件,以确保应用程序不会因存储空间不足而受到影响

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类并重写其方法,我们可以定义服务端的实现逻辑。

  1. addData(String tag, byte[] data, int flags):允许客户端向DropBoxManagerService添加字节数据,这些数据会被标记为指定的tag,并附带一些标志(flags)。
  2. addFile(String tag, ParcelFileDescriptor fd, int flags):允许客户端向服务添加文件,通过ParcelFileDescriptor提供文件描述符,以便服务可以访问该文件。
  3. isTagEnabled(String tag):检查指定tag的日志是否已启用。
  4. getNextEntry(String tag, long millis, String callingPackage)和5. getNextEntryWithAttribution(String tag, long millis, String callingPackage, String callingAttributionTag):这两个方法用于获取指定tag的下一个日志条目。后者还允许指定调用者的归因标签。
  5. dump(FileDescriptor fd, PrintWriter pw, String[] args):允许服务将其状态或数据输出到给定的文件描述符中,通常用于调试或日志记录。
  6. 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");
        }
    }
  1. ShellCmd类的主要作用是处理与DropBoxManagerService相关的shell命令。在onCommand方法中,它首先检查传入的命令(cmd)是否为null,如果是,则调用handleDefaultCommands(cmd)来处理默认命令。
  2. 对于非空的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)
  3. onHelp方法用于提供关于这些命令的帮助信息。它打印出每个命令的简短描述和用法,以便用户了解如何与DropBoxManagerService进行交互
  4. 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的值
         * 每次发送一个消息&#
  • 20
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值