Android Service之设备存储空间监控 DeviceStorageMonitorService

Android Service之设备存储空间监控

在负责文件系统模块的过程中,经常会碰到由于系统空间被消耗完而导致的问题,因此要确保为系统功能(如数据库同步)保留一定的空间。在功能机中一般是由文件系统模块预留,那么在Android系统是怎样对设备存储空间进行管理和监控的呢?

如果你在使用Android手机时有过把memory填满或者即将填满的经历,也许你会注意到在这种情况下手机的Notifications栏会有“Storage space running out”的通知。当点开该通知你会发现Setting-->Storage settings –>Device memory 下会有如下提示:Not enough storage space.

这个服务的实现是在frameworks\base\services\core\java\com\android\server\storage\DeviceStorageMonitorService.java。DeviceStorageMonitorService类实现了一个监控设备上存储空间的服务。如果设备的剩余存储空间小于某一个阀值(默认是存储空间的10%,这个值可以设置)时将会向用户发送剩余空间不足的警告,让用户释放一些空间。

下面就分析一下这个类。首先看一下该服务是如何被添加进来的。在frameworks\base\services\java\com\android\server\SystemServer.java中使用ServiceManager.addService()来添加系统服务: startOtherServices

t.traceBegin("StartDeviceMonitor");
mSystemServiceManager.startService(DeviceStorageMonitorService.class);
t.traceEnd();

构造方法

通过handler 轮询check 设备存储使用情况

    public DeviceStorageMonitorService(Context context) {
        super(context);

        mHandlerThread = new HandlerThread(TAG, android.os.Process.THREAD_PRIORITY_BACKGROUND);
        mHandlerThread.start();

        mHandler = new Handler(mHandlerThread.getLooper()) {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case MSG_CHECK:
                        check();
                        return;
                }
            }
        };
    }


    @Override
    public void onStart() {
        final Context context = getContext();
        mNotifManager = context.getSystemService(NotificationManager.class);

        mCacheFileDeletedObserver = new CacheFileDeletedObserver();
        mCacheFileDeletedObserver.startWatching();

        // Ensure that the notification channel is set up
        PackageManager packageManager = context.getPackageManager();
        boolean isTv = packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK);

        if (isTv) { 盒子属于TV  创建NotificationChannel
            mNotifManager.createNotificationChannel(new NotificationChannel(
                    TV_NOTIFICATION_CHANNEL_ID,
                    context.getString(
                        com.android.internal.R.string.device_storage_monitor_notification_channel),
                    NotificationManager.IMPORTANCE_HIGH));
        }
     注册服务
        publishBinderService(SERVICE, mRemoteService);
        publishLocalService(DeviceStorageMonitorInternal.class, mLocalService);

        // Kick off pass to examine storage state
        mHandler.removeMessages(MSG_CHECK);
        mHandler.obtainMessage(MSG_CHECK).sendToTarget();
    }

 

    /**
     * Core logic that checks the storage state of every mounted private volume.
     * Since this can do heavy I/O, callers should invoke indirectly using
     * {@link #MSG_CHECK}.
     */
    @WorkerThread
    private void check() {
        final StorageManager storage = getContext().getSystemService(StorageManager.class);
        final int seq = mSeq.get();

        // Check every mounted private volume to see if they're low on space
        for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
            final File file = vol.getPath();
       获取系统设定的存储阈值 final
long fullBytes = storage.getStorageFullBytes(file); final long lowBytes = storage.getStorageLowBytes(file); // Automatically trim cached data when nearing the low threshold; // when it's within 150% of the threshold, we try trimming usage // back to 200% of the threshold. if (file.getUsableSpace() < (lowBytes * 3) / 2) { final PackageManagerService pms = (PackageManagerService) ServiceManager .getService("package"); try { pms.freeStorage(vol.getFsUuid(), lowBytes * 2, 0); } catch (IOException e) { Slog.w(TAG, e); } } // Send relevant broadcasts and show notifications based on any // recently noticed state transitions. final UUID uuid = StorageManager.convert(vol.getFsUuid()); final State state = findOrCreateState(uuid); final long totalBytes = file.getTotalSpace(); final long usableBytes = file.getUsableSpace(); int oldLevel = state.level; int newLevel;
      获取设备存储状态
if (mForceLevel != State.LEVEL_UNKNOWN) { // When in testing mode, use unknown old level to force sending // of any relevant broadcasts. oldLevel = State.LEVEL_UNKNOWN; newLevel = mForceLevel; } else if (usableBytes <= fullBytes) { newLevel = State.LEVEL_FULL; } else if (usableBytes <= lowBytes) { newLevel = State.LEVEL_LOW; } else if (StorageManager.UUID_DEFAULT.equals(uuid) && !isBootImageOnDisk() && usableBytes < BOOT_IMAGE_STORAGE_REQUIREMENT) { newLevel = State.LEVEL_LOW; } else { newLevel = State.LEVEL_NORMAL; } // Log whenever we notice drastic storage changes if ((Math.abs(state.lastUsableBytes - usableBytes) > DEFAULT_LOG_DELTA_BYTES) || oldLevel != newLevel) { EventLogTags.writeStorageState(uuid.toString(), oldLevel, newLevel, usableBytes, totalBytes); state.lastUsableBytes = usableBytes; }         发送通知和广播 updateNotifications(vol, oldLevel, newLevel); updateBroadcasts(vol, oldLevel, newLevel, seq); state.level = newLevel; } // Loop around to check again in future; we don't remove messages since // there might be an immediate request pending. if (!mHandler.hasMessages(MSG_CHECK)) { mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CHECK), DEFAULT_CHECK_INTERVAL); } }

StorageManager

位于 frameworks\base\core\java\android\os\storage\StorageManager.java 获取系统设置的阈值

   /**
     * Return the number of available bytes at which the given path is
     * considered full.
     *
     * @hide
     */
    @UnsupportedAppUsage
    public long getStorageFullBytes(File path) {
        return Settings.Global.getLong(mResolver, Settings.Global.SYS_STORAGE_FULL_THRESHOLD_BYTES,
                DEFAULT_FULL_THRESHOLD_BYTES);
    }
    /**
     * Return the number of available bytes at which the given path is
     * considered running low on storage.
     *
     * @hide
     */
    @UnsupportedAppUsage
    public long getStorageLowBytes(File path) {
        final long lowPercent = Settings.Global.getInt(mResolver,
                Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE, DEFAULT_THRESHOLD_PERCENTAGE);
        final long lowBytes = (path.getTotalSpace() * lowPercent) / 100;

        final long maxLowBytes = Settings.Global.getLong(mResolver,
                Settings.Global.SYS_STORAGE_THRESHOLD_MAX_BYTES, DEFAULT_THRESHOLD_MAX_BYTES);

        return Math.min(lowBytes, maxLowBytes);
    }

Settings字段

位于 frameworks\base\core\java\android\provider\Settings.java

   /**
         * Maximum byte size of the low storage threshold. This is to ensure
         * that {@link #SYS_STORAGE_THRESHOLD_PERCENTAGE} does not result in an
         * overly large threshold for large storage devices. Currently this must
         * be less than 2GB. This default is 500MB.
         *
         * @hide
         */
        public static final String
                SYS_STORAGE_THRESHOLD_MAX_BYTES = "sys_storage_threshold_max_bytes";

        /**
         * Minimum bytes of free storage on the device before the data partition
         * is considered full. By default, 1 MB is reserved to avoid system-wide
         * SQLite disk full exceptions.
         *
         * @hide
         */
        public static final String
                SYS_STORAGE_FULL_THRESHOLD_BYTES = "sys_storage_full_threshold_bytes";

可以使用串口命令进行调整

settings put global sys_storage_threshold_percentage 95
settings put global sys_storage_threshold_max_bytes 5000000000

SettingsProvider的数据保存在文件/data/system/users/0/settings_***.xml和数据库settings.db中

           写入顺序                    key                    value         写入的apk包名
<setting id="177" name="sys_storage_threshold_percentage" value="95" package="com.android.shell" />

 Android Service之设备存储空间监控 - kaffeel - 博客园 (cnblogs.com)

posted @ 2023-03-31 11:09  xiaowang_lj  阅读(307)  评论(0编辑  收藏  举报