一、Android 版本
Android 13
二、low storage简介(DeviceStorageMonitorService)
设备存储监视器服务是一个模块,主要用来:
1.监视设备存储(“/ data”)。
2.每60秒扫描一次免费存储空间(谷歌默认值)
3.当设备的存储空间不足时生成“低存储”通知。 //updateNotifications
4.引导用户管理设备中安装的所有应用程序,并发送意图。 //updateBroadcasts
5.存储严重不足时显示警告对话框。
6.为AMS/PMS提供公共API以查询存储状态
三、DeviceStorageMonitorService关键代码介绍
3.1服务初始化
- DeviceStorageMonitorService初始化handler用于check
- SystemService.onStart()时,获取通知,添加devicestoragemonitor到Binder Service
- 执行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