Android Low Storage机制之DeviceStorageMonitorService

一、Android 版本

Android 13

二、low storage简介(DeviceStorageMonitorService)

设备存储监视器服务是一个模块,主要用来:

1.监视设备存储(“/ data”)。
2.每60秒扫描一次免费存储空间(谷歌默认值)
3.当设备的存储空间不足时生成“低存储”通知。 //updateNotifications
4.引导用户管理设备中安装的所有应用程序,并发送意图。 //updateBroadcasts
5.存储严重不足时显示警告对话框。
6.为AMS/PMS提供公共API以查询存储状态

三、DeviceStorageMonitorService关键代码介绍

3.1服务初始化
  1. DeviceStorageMonitorService初始化handler用于check
  2. SystemService.onStart()时,获取通知,添加devicestoragemonitor到Binder Service
  3. 执行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_LOW:
                        checkLow();
                        return;
                    case MSG_CHECK_HIGH:
                        checkHigh();
                        return;
                }
            }
        };
    }
    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) {
            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_LOW);
        mHandler.obtainMessage(MSG_CHECK_LOW).sendToTarget();
    }
3.2 check  data分区

frameworks/base/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java

注意这两个方法

if (State.isEntering(State.LEVEL_LOW, oldLevel, newLevel))

if (State.isLeaving(State.LEVEL_LOW, oldLevel, newLevel))

主要逻辑是进入低内存的时候,判断之前的状态,如果之前是正常的,就发通知,如果一直处理低内存状态,就不处理,避免重发通知处理

如果是低内存到正常内存状态,那就自动把通知取消掉

    @WorkerThread
    private void checkLow() {
        Slog.w(TAG, "AAAAA >>> checkLow()");
        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()) {
            Slog.w(TAG, "AAAAA >>> checkLow() updateNotifications");
            final File file = vol.getPath();
            final long fullBytes = storage.getStorageFullBytes(file);
            final long lowBytes = storage.getStorageLowBytes(file);//默认500M或总空间的%5

            // 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 PackageManagerInternal pm =
                        LocalServices.getService(PackageManagerInternal.class);
                //lowBytes的1.5倍容量时触发freeStorage
                try {
                    pm.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();//data总大小
            final long usableBytes = file.getUsableSpace();//可使用大小

            int oldLevel = state.level;
            int newLevel;

            //判断是LEVEL_LOW,LEVEL_FULL还是LEVEL_NORMAL
            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)
                    && 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;
        }

        //没有check消息,继续60s检测一次
        // 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_LOW)) {
            mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CHECK_LOW),
                    LOW_CHECK_INTERVAL);
        }
        if (!mHandler.hasMessages(MSG_CHECK_HIGH)) {
            mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CHECK_HIGH),
                    HIGH_CHECK_INTERVAL);
        }
    }
 private void updateNotifications(VolumeInfo vol, int oldLevel, int newLevel) {
        Slog.w(TAG, "AAAAA >>> updateNotifications() oldLevel="+oldLevel+",,,,,newLevel="+newLevel);
        final Context context = getContext();
        final UUID uuid = StorageManager.convert(vol.getFsUuid());

        if (State.isEntering(State.LEVEL_LOW, oldLevel, newLevel)) {
            //调起通知
             Slog.w(TAG, "AAAAA >>> updateNotifications() do Notification");
            Intent lowMemIntent = new Intent(StorageManager.ACTION_MANAGE_STORAGE);
            lowMemIntent.putExtra(StorageManager.EXTRA_UUID, uuid);
            lowMemIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

            final CharSequence title = context.getText(
                    com.android.internal.R.string.low_internal_storage_view_title);

            final CharSequence details = context.getText(
                    com.android.internal.R.string.low_internal_storage_view_text);

            PendingIntent intent = PendingIntent.getActivityAsUser(context, 0, lowMemIntent,
                    PendingIntent.FLAG_IMMUTABLE, null, UserHandle.CURRENT);
            Notification notification =
                    new Notification.Builder(context, SystemNotificationChannels.ALERTS)
                            .setSmallIcon(com.android.internal.R.drawable.stat_notify_disk_full)
                            .setTicker(title)
                            .setColor(context.getColor(
                                com.android.internal.R.color.system_notification_accent_color))
                            .setContentTitle(title)
                            .setContentText(details)
                            .setContentIntent(intent)
                            .setStyle(new Notification.BigTextStyle()
                                  .bigText(details))
                            .setVisibility(Notification.VISIBILITY_PUBLIC)
                            .setCategory(Notification.CATEGORY_SYSTEM)
                            .extend(new Notification.TvExtender()
                                    .setChannelId(TV_NOTIFICATION_CHANNEL_ID))
                            .build();
            notification.flags |= Notification.FLAG_NO_CLEAR;
            mNotifManager.notifyAsUser(uuid.toString(), SystemMessage.NOTE_LOW_STORAGE,
                    notification, UserHandle.ALL);
            FrameworkStatsLog.write(FrameworkStatsLog.LOW_STORAGE_STATE_CHANGED,
                    Objects.toString(vol.getDescription()),
                    FrameworkStatsLog.LOW_STORAGE_STATE_CHANGED__STATE__ON);
        } else if (State.isLeaving(State.LEVEL_LOW, oldLevel, newLevel)) {
            //取消通知
            Slog.w(TAG, "AAAAA >>> updateNotifications() cancel Notification");
            mNotifManager.cancelAsUser(uuid.toString(), SystemMessage.NOTE_LOW_STORAGE,
                    UserHandle.ALL);
            FrameworkStatsLog.write(FrameworkStatsLog.LOW_STORAGE_STATE_CHANGED,
                    Objects.toString(vol.getDescription()),
                    FrameworkStatsLog.LOW_STORAGE_STATE_CHANGED__STATE__OFF);
        }
    }
    private void updateBroadcasts(VolumeInfo vol, int oldLevel, int newLevel, int seq) {
        if (!Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, vol.getFsUuid())) {
            // We don't currently send broadcasts for secondary volumes
            return;
        }
        //lowStorage广播action  ACTION_DEVICE_STORAGE_LOW
        final Intent lowIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_LOW)
                .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
                        | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
                        | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS)
                .putExtra(EXTRA_SEQUENCE, seq);
        //正常Storage广播action  ACTION_DEVICE_STORAGE_OK
        final Intent notLowIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_OK)
                .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
                        | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
                        | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS)
                .putExtra(EXTRA_SEQUENCE, seq);
        
        if (State.isEntering(State.LEVEL_LOW, oldLevel, newLevel)) {
            //只发送一次广播ACTION_DEVICE_STORAGE_LOW,粘性广播,进程注册肯定会收到广播
            getContext().sendStickyBroadcastAsUser(lowIntent, UserHandle.ALL);
        } else if (State.isLeaving(State.LEVEL_LOW, oldLevel, newLevel)) {
            //恢复正常移除lowIntent粘性广播,发送normal的普通广播
            getContext().removeStickyBroadcastAsUser(lowIntent, UserHandle.ALL);
            getContext().sendBroadcastAsUser(notLowIntent, UserHandle.ALL);
        }

        final Intent fullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_FULL)
                .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT)
                .putExtra(EXTRA_SEQUENCE, seq);
        final Intent notFullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_NOT_FULL)
                .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT)
                .putExtra(EXTRA_SEQUENCE, seq);

        //发送FULL Storage广播ACTION_DEVICE_STORAGE_FULL
        if (State.isEntering(State.LEVEL_FULL, oldLevel, newLevel)) {
            getContext().sendStickyBroadcastAsUser(fullIntent, UserHandle.ALL);
        } else if (State.isLeaving(State.LEVEL_FULL, oldLevel, newLevel)) {
            getContext().removeStickyBroadcastAsUser(fullIntent, UserHandle.ALL);
            getContext().sendBroadcastAsUser(notFullIntent, UserHandle.ALL);
        }
    }
    private static class State {
       private static final int LEVEL_UNKNOWN = -1;
       private static final int LEVEL_NORMAL = 0;//空间正常
       private static final int LEVEL_LOW = 1;//空间低
       private static final int LEVEL_FULL = 2;//空间满了
}
3.3 低内存判断值

frameworks/base/core/java/android/os/storage/StorageManager.java

  //总空间的百分比(默认是5%)和 500M 的最小值 作为最低空间提醒的标准
 public static final int DEFAULT_STORAGE_THRESHOLD_PERCENT_LOW = 5;
  private static final long DEFAULT_THRESHOLD_MAX_BYTES = DataUnit.MEBIBYTES.toBytes(500);
  
  public long getStorageLowBytes(File path) {
        final long lowPercent = Settings.Global.getInt(mResolver,
                Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE,
                DEFAULT_STORAGE_THRESHOLD_PERCENT_LOW);
        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);
    }

四、查看剩余空间的方法

1、df -h 查看 data 目录空间


 

2、从桌面"设置"应用中查看存储

自动填满磁盘空间apk:https://download.csdn.net/download/banzhuantuqiang/89331283

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/639842.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

prometheusgrafananode_export搭建监控平台

一、环境要求 1、docker安装docker环境 2、docker安装prometheus 3、docker安装grafana 4、node-exportor(安装在被测服务器上) 5、我的服务器是Ubuntu 二、docker 安装prometheus 1、下载Prometheus镜像 docker pull prom/prometheus 2、检查端口是否被占用 安装netstat命…

Vitis HLS 学习笔记--抽象并行编程模型-控制驱动与数据驱动

目录 1. 简介 2. Takeaways 3. Data-driven Task-level Parallelism 3.1 simple_data_driven 示例 3.2 分析 hls::task 类 3.3 分析通道(Channel) 3.4 注意死锁 4. Control-driven Task-level Parallelism 4.1 理解控制驱动的 TLP 4.2 simple_control_driven 示例 4…

开源实用!猫抓媒体嗅探浏览器插件

CatCatch&#xff1a;网络资源&#xff0c;一触即发 - 精选真开源&#xff0c;释放新价值。 概览 CatCatch是一个专为浏览器设计的资源嗅探扩展&#xff0c;旨在帮助用户轻松捕获和分析网页中的各种资源。无论是视频、音频还是其他类型的文件&#xff0c;猫爪都能提供直观的界…

C++青少年简明教程:If选择语句

C青少年简明教程&#xff1a;If选择语句 C中选择语句的语法是&#xff1a; if (条件) { 条件成立时需要执行的语句... } [else { 条件不成立时需要执行的语句... }] 说明&#xff1a; if后面使用一个括号&#xff0c;括号里是条件——关系表达式。 所谓的关系表达式就是判…

Java基础之异常(简单易懂)

异常 1.JAVA异常体系 &#xff08;1&#xff09;Throwable类(表示可抛)是所有异常和错误的超类&#xff0c;两个直接子类为Error和Exception,分别表示错误和异常;其中异常类Exception又分为运行时异常和非运行时异常&#xff0c;这两个异常有很大区别&#xff0c;运行时异常也…

生成式AI改变的不只是应用场景,而是要重塑整个行业

即使是在各种创新技术不断涌现的IT行业&#xff0c;生成式AI也可以当之无愧地说是当前全球最受关注的焦点领域&#xff0c;没有之一。 那么对于企业来说&#xff0c;生成式AI技术究竟可以为他们带来怎样的变化和革新&#xff1f;企业又能从中获得怎样的收益&#xff1f; 作为一…

靠着单干实现财富自由,可太爽了

这里所说的“单干”&#xff0c;并不是单打独斗的意思&#xff0c;而是一种商业认知&#xff0c;以及由这种认知衍生出来的商业模式、商业方法和商业实践。 之前提到单干&#xff0c;会本能地以为它是指脱离公司等组织形式&#xff0c;自己一个人做生意。现在单干有了更丰富的…

医院门诊互联电子病历|基于SSM+vue的医院门诊互联电子病历管理信息系统的设计与实现(源码+数据库+文档)

医院门诊互联电子病历管理信息系统 目录 基于SSM&#xff0b;vue的医院门诊互联电子病历管理信息系统的设计与实现 一、前言 二、系统设计 三、系统功能设计 1系统功能模块 2后台登录模块 5.2.1管理员功能 5.2.2用户功能 5.2.3医生功能 四、数据库设计 五、核心代码…

源网络地址转换SNAT

左上角的是访问互联网发送的数据包&#xff0c;第一个是访问&#xff0c;第二个是网页传回来的 3、4项是源端口号和目的端口号&#xff08;3是随机的&#xff08;1024-65535&#xff09;&#xff0c;那个是http的网页服务端口就是80&#xff09; 那么往回传数据的时候源和目的…

[排序算法]2. 图解选择排序及其代码实现

选择排序 选择排序就是重复“从待排序的数据中寻找最小值&#xff0c;将其与序列最左边的数字进行交换”这一操作的算法。在序列中寻找最小值时使用的是线性查找。 算法步骤: 1.使用线性查找在数据中寻找最小值, 于是我们找到了最小值1 2.将最小值1与序列最左边的…

【SqL】数据库脚本编写规范和指南

编写本文档的目的是保证在开发过程中产出高效、格式统一、易阅读、易维护的SQL代码。 1 编写目的 2 SQL书写规范 3 SQL编写原则 软件全套资料获取进主页或者本文末个人名片直接获取。

Nodejs及stfshow相关例题

Nodejs及stfshow相关例题 Node.js 是一个基于 Chrome V8 引擎的 Javascript 运行环境。可以说nodejs是一个运行环境&#xff0c;或者说是一个 JS 语言解释器而不是某种库。 Node.js可以生成动态页面内容Node.js 可以在服务器上创建、打开、读取、写入、删除和关闭文件Node.js…

panic对defer语句的执行的影响

1.主线程中的panic会直接导致所有正在运行的go协程无法执行,还会导致声明在它之后的defer语句无法执行。 package mainimport ("fmt""time" )func main() {defer fmt.Println("defer1") //声明在panic之前的defer会执行go func() {defer fmt.Pri…

微信小程序反编译/解包

微信小程序反编译/解包 环境与工具 操作系统&#xff1a;Windows 11 23H2 微信版本&#xff1a;3.9.10.19 Q&#xff1a;如何找到小程序文件位置&#xff1f; A&#xff1a;在微信的设置找到文件路径&#xff0c;小程序文件位于 \WeChat Files\Applet\。 Q&#xff1a;小程…

STM32笔记-AD模数转换

目录 一、ADC介绍 二、ADC主要特征 三、ADC框图 1. ​​​​ 外部触发转换 ​ 2. 转换模式 3. 输入通道 4. 逻辑框图 四、校准 五、数据对齐 六、AD转换步骤 七、AD_Init(单通道AD转换)初始化函数配置 DMA: adc_dma_mode_enable(ADC0); 这段代码是用来使能ADC的DMA&a…

怎么一键消除路人?教你三个消除方法

怎么一键消除路人&#xff1f;在数字时代&#xff0c;摄影已成为我们记录生活、表达情感的重要方式。然而&#xff0c;完美的照片背后往往隐藏着一些不那么完美的元素——比如那些不经意间闯入镜头的路人。他们或许只是匆匆过客&#xff0c;但却足以破坏你精心构图的美好瞬间。…

视觉SLAM十四讲:从理论到实践(Chapter5:相机与图像)

前言 学习笔记&#xff0c;仅供学习&#xff0c;不做商用&#xff0c;如有侵权&#xff0c;联系我删除即可 目标 理解针孔相机的模型、内参与径向畸变参数。理解一个空间点是如何投影到相机成像平面的。掌握OpenCV的图像存储与表达方式。学会基本的摄像头标定方法。 一、相…

PS —— 精修图像

PS —— 精修图像 修复污点修复画笔工具修复画笔工具 美白滤镜去杂锐化加杂减淡和锐化工具 我觉得今天这篇博客&#xff0c;无论是男同胞还是女同胞&#xff0c;都要熟练掌握&#xff08;哈哈哈哈…) 今天我们来学习如何精修图像&#xff0c;精修图像一般分为几步——修复&…

5.24学习记录

[FSCTF 2023]ez_php2 比较简单的pop链 <?php highlight_file(__file__); Class Rd{public $ending;public $cl;public $poc;public function __destruct(){echo "All matters have concluded";die($this->ending);}public function __call($name, $arg){for…

Sap Hana 数据迁移同步优化(二)

简述 CloudCanal 近期对 Hana 源端链路做了新一轮优化&#xff0c;这篇文章简要做下分享。 本轮优化主要包含: 表级别 CDC 表表级别任务位点表级别触发器 单 CDC 表的问题 CloudCanal 在实现 Hana 源端增量同步时&#xff0c;最初采用的是单 CDC 表的模式。 即所有订阅表…