/* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.settingslib.deviceinfo; import android.app.usage.ExternalStorageStats; import android.app.usage.StorageStats; import android.app.usage.StorageStatsManager; import android.content.Context; import android.content.pm.UserInfo; import android.os.AsyncTask; import android.os.Environment; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; import android.os.storage.StorageVolume; import android.os.storage.VolumeInfo; import android.util.Log; import android.util.SparseArray; import android.util.SparseLongArray; import java.io.IOException; import java.lang.ref.WeakReference; import java.util.HashMap; import java.util.List; /** * Utility for measuring the disk usage of internal storage or a physical * {@link StorageVolume}. */ public class StorageMeasurement { private static final String TAG = "StorageMeasurement"; public static class MeasurementDetails { /** Size of storage device. */ public long totalSize; /** Size of available space. */ public long availSize; /** Size of all cached data. */ public long cacheSize; /** * Total disk space used by everything. *

* Key is {@link UserHandle}. */ public SparseLongArray usersSize = new SparseLongArray(); /** * Total disk space used by apps. *

* Key is {@link UserHandle}. */ public SparseLongArray appsSize = new SparseLongArray(); /** * Total disk space used by media on shared storage. *

* First key is {@link UserHandle}. Second key is media type, such as * {@link Environment#DIRECTORY_PICTURES}. */ public SparseArray> mediaSize = new SparseArray<>(); /** * Total disk space used by non-media on shared storage. *

* Key is {@link UserHandle}. */ public SparseLongArray miscSize = new SparseLongArray(); @Override public String toString() { return "MeasurementDetails: [totalSize: " + totalSize + " availSize: " + availSize + " cacheSize: " + cacheSize + " mediaSize: " + mediaSize + " miscSize: " + miscSize + "usersSize: " + usersSize + "]"; } } public interface MeasurementReceiver { void onDetailsChanged(MeasurementDetails details); } private WeakReference mReceiver; private final Context mContext; private final UserManager mUser; private final StorageStatsManager mStats; private final VolumeInfo mVolume; private final VolumeInfo mSharedVolume; public StorageMeasurement(Context context, VolumeInfo volume, VolumeInfo sharedVolume) { mContext = context.getApplicationContext(); mUser = mContext.getSystemService(UserManager.class); mStats = mContext.getSystemService(StorageStatsManager.class); mVolume = volume; mSharedVolume = sharedVolume; } public void setReceiver(MeasurementReceiver receiver) { if (mReceiver == null || mReceiver.get() == null) { mReceiver = new WeakReference(receiver); } } public void forceMeasure() { measure(); } public void measure() { new MeasureTask().execute(); } public void onDestroy() { mReceiver = null; } private class MeasureTask extends AsyncTask { @Override protected MeasurementDetails doInBackground(Void... params) { return measureExactStorage(); } @Override protected void onPostExecute(MeasurementDetails result) { final MeasurementReceiver receiver = (mReceiver != null) ? mReceiver.get() : null; if (receiver != null) { receiver.onDetailsChanged(result); } } } private MeasurementDetails measureExactStorage() { final List users = mUser.getUsers(); final long start = SystemClock.elapsedRealtime(); final MeasurementDetails details = new MeasurementDetails(); if (mVolume == null) return details; if (mVolume.getType() == VolumeInfo.TYPE_PUBLIC) { details.totalSize = mVolume.getPath().getTotalSpace(); details.availSize = mVolume.getPath().getUsableSpace(); return details; } try { details.totalSize = mStats.getTotalBytes(mVolume.fsUuid); details.availSize = mStats.getFreeBytes(mVolume.fsUuid); } catch (IOException e) { // The storage volume became null while we were measuring it. Log.w(TAG, e); return details; } final long finishTotal = SystemClock.elapsedRealtime(); Log.d(TAG, "Measured total storage in " + (finishTotal - start) + "ms"); if (mSharedVolume != null && mSharedVolume.isMountedReadable()) { for (UserInfo user : users) { final HashMap mediaMap = new HashMap<>(); details.mediaSize.put(user.id, mediaMap); final ExternalStorageStats stats; try { stats = mStats.queryExternalStatsForUser(mSharedVolume.fsUuid, UserHandle.of(user.id)); } catch (IOException e) { Log.w(TAG, e); continue; } addValue(details.usersSize, user.id, stats.getTotalBytes()); // Track detailed data types mediaMap.put(Environment.DIRECTORY_MUSIC, stats.getAudioBytes()); mediaMap.put(Environment.DIRECTORY_MOVIES, stats.getVideoBytes()); mediaMap.put(Environment.DIRECTORY_PICTURES, stats.getImageBytes()); final long miscBytes = stats.getTotalBytes() - stats.getAudioBytes() - stats.getVideoBytes() - stats.getImageBytes(); addValue(details.miscSize, user.id, miscBytes); } } final long finishShared = SystemClock.elapsedRealtime(); Log.d(TAG, "Measured shared storage in " + (finishShared - finishTotal) + "ms"); if ((mVolume.getType() == VolumeInfo.TYPE_PRIVATE) && mVolume.isMountedReadable()) { for (UserInfo user : users) { final StorageStats stats; try { stats = mStats.queryStatsForUser(mVolume.fsUuid, UserHandle.of(user.id)); } catch (IOException e) { Log.w(TAG, e); continue; } // Only count code once against current user if (user.id == UserHandle.myUserId()) { addValue(details.usersSize, user.id, stats.getCodeBytes()); } addValue(details.usersSize, user.id, stats.getDataBytes()); addValue(details.appsSize, user.id, stats.getCodeBytes() + stats.getDataBytes()); details.cacheSize += stats.getCacheBytes(); } } final long finishPrivate = SystemClock.elapsedRealtime(); Log.d(TAG, "Measured private storage in " + (finishPrivate - finishShared) + "ms"); return details; } private static void addValue(SparseLongArray array, int key, long value) { array.put(key, array.get(key) + value); } }