Merge "Move StorageMeasurement to SettingsLib"
This commit is contained in:
@@ -57,9 +57,10 @@ import com.android.settings.Settings.StorageUseActivity;
|
|||||||
import com.android.settings.SettingsPreferenceFragment;
|
import com.android.settings.SettingsPreferenceFragment;
|
||||||
import com.android.settings.Utils;
|
import com.android.settings.Utils;
|
||||||
import com.android.settings.applications.ManageApplications;
|
import com.android.settings.applications.ManageApplications;
|
||||||
import com.android.settings.deviceinfo.StorageMeasurement.MeasurementDetails;
|
|
||||||
import com.android.settings.deviceinfo.StorageMeasurement.MeasurementReceiver;
|
|
||||||
import com.android.settings.deviceinfo.StorageSettings.MountTask;
|
import com.android.settings.deviceinfo.StorageSettings.MountTask;
|
||||||
|
import com.android.settingslib.deviceinfo.StorageMeasurement;
|
||||||
|
import com.android.settingslib.deviceinfo.StorageMeasurement.MeasurementDetails;
|
||||||
|
import com.android.settingslib.deviceinfo.StorageMeasurement.MeasurementReceiver;
|
||||||
import com.google.android.collect.Lists;
|
import com.google.android.collect.Lists;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
@@ -1,441 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.settings.deviceinfo;
|
|
||||||
|
|
||||||
import android.app.ActivityManager;
|
|
||||||
import android.content.ComponentName;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.ServiceConnection;
|
|
||||||
import android.content.pm.ApplicationInfo;
|
|
||||||
import android.content.pm.IPackageStatsObserver;
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.content.pm.PackageStats;
|
|
||||||
import android.content.pm.UserInfo;
|
|
||||||
import android.os.Environment;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.HandlerThread;
|
|
||||||
import android.os.IBinder;
|
|
||||||
import android.os.Looper;
|
|
||||||
import android.os.Message;
|
|
||||||
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.SparseLongArray;
|
|
||||||
|
|
||||||
import com.android.internal.app.IMediaContainerService;
|
|
||||||
import com.android.internal.util.ArrayUtils;
|
|
||||||
import com.google.android.collect.Sets;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.lang.ref.WeakReference;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Utility for measuring the disk usage of internal storage or a physical
|
|
||||||
* {@link StorageVolume}. Connects with a remote {@link IMediaContainerService}
|
|
||||||
* and delivers results to {@link MeasurementReceiver}.
|
|
||||||
*/
|
|
||||||
public class StorageMeasurement {
|
|
||||||
private static final String TAG = "StorageMeasurement";
|
|
||||||
|
|
||||||
private static final boolean LOCAL_LOGV = true;
|
|
||||||
static final boolean LOGV = LOCAL_LOGV && Log.isLoggable(TAG, Log.VERBOSE);
|
|
||||||
|
|
||||||
private static final String DEFAULT_CONTAINER_PACKAGE = "com.android.defcontainer";
|
|
||||||
|
|
||||||
public static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
|
|
||||||
DEFAULT_CONTAINER_PACKAGE, "com.android.defcontainer.DefaultContainerService");
|
|
||||||
|
|
||||||
/** Media types to measure on external storage. */
|
|
||||||
private static final Set<String> sMeasureMediaTypes = Sets.newHashSet(
|
|
||||||
Environment.DIRECTORY_DCIM, Environment.DIRECTORY_MOVIES,
|
|
||||||
Environment.DIRECTORY_PICTURES, Environment.DIRECTORY_MUSIC,
|
|
||||||
Environment.DIRECTORY_ALARMS, Environment.DIRECTORY_NOTIFICATIONS,
|
|
||||||
Environment.DIRECTORY_RINGTONES, Environment.DIRECTORY_PODCASTS,
|
|
||||||
Environment.DIRECTORY_DOWNLOADS, Environment.DIRECTORY_ANDROID);
|
|
||||||
|
|
||||||
public static class MeasurementDetails {
|
|
||||||
/**
|
|
||||||
* Total apps disk usage.
|
|
||||||
* <p>
|
|
||||||
* When measuring internal storage, this value includes the code size of
|
|
||||||
* all apps (regardless of install status for current user), and
|
|
||||||
* internal disk used by the current user's apps. When the device
|
|
||||||
* emulates external storage, this value also includes emulated storage
|
|
||||||
* used by the current user's apps.
|
|
||||||
* <p>
|
|
||||||
* When measuring a physical {@link StorageVolume}, this value includes
|
|
||||||
* usage by all apps on that volume.
|
|
||||||
*/
|
|
||||||
public long appsSize;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Total cache disk usage by apps.
|
|
||||||
*/
|
|
||||||
public long cacheSize;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Total media disk usage, categorized by types such as
|
|
||||||
* {@link Environment#DIRECTORY_MUSIC}.
|
|
||||||
* <p>
|
|
||||||
* When measuring internal storage, this reflects media on emulated
|
|
||||||
* storage for the current user.
|
|
||||||
* <p>
|
|
||||||
* When measuring a physical {@link StorageVolume}, this reflects media
|
|
||||||
* on that volume.
|
|
||||||
*/
|
|
||||||
public HashMap<String, Long> mediaSize = new HashMap<>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Misc external disk usage for the current user, unaccounted in
|
|
||||||
* {@link #mediaSize}.
|
|
||||||
*/
|
|
||||||
public long miscSize;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Total disk usage for users, which is only meaningful for emulated
|
|
||||||
* internal storage. Key is {@link UserHandle}.
|
|
||||||
*/
|
|
||||||
public SparseLongArray usersSize = new SparseLongArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface MeasurementReceiver {
|
|
||||||
public void onDetailsChanged(MeasurementDetails details);
|
|
||||||
}
|
|
||||||
|
|
||||||
private WeakReference<MeasurementReceiver> mReceiver;
|
|
||||||
|
|
||||||
private final Context mContext;
|
|
||||||
|
|
||||||
private final VolumeInfo mVolume;
|
|
||||||
private final VolumeInfo mSharedVolume;
|
|
||||||
|
|
||||||
private final MainHandler mMainHandler;
|
|
||||||
private final MeasurementHandler mMeasurementHandler;
|
|
||||||
|
|
||||||
public StorageMeasurement(Context context, VolumeInfo volume, VolumeInfo sharedVolume) {
|
|
||||||
mContext = context.getApplicationContext();
|
|
||||||
|
|
||||||
mVolume = volume;
|
|
||||||
mSharedVolume = sharedVolume;
|
|
||||||
|
|
||||||
// Start the thread that will measure the disk usage.
|
|
||||||
final HandlerThread handlerThread = new HandlerThread("MemoryMeasurement");
|
|
||||||
handlerThread.start();
|
|
||||||
|
|
||||||
mMainHandler = new MainHandler();
|
|
||||||
mMeasurementHandler = new MeasurementHandler(handlerThread.getLooper());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setReceiver(MeasurementReceiver receiver) {
|
|
||||||
if (mReceiver == null || mReceiver.get() == null) {
|
|
||||||
mReceiver = new WeakReference<MeasurementReceiver>(receiver);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void forceMeasure() {
|
|
||||||
invalidate();
|
|
||||||
measure();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void measure() {
|
|
||||||
if (!mMeasurementHandler.hasMessages(MeasurementHandler.MSG_MEASURE)) {
|
|
||||||
mMeasurementHandler.sendEmptyMessage(MeasurementHandler.MSG_MEASURE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onDestroy() {
|
|
||||||
mReceiver = null;
|
|
||||||
mMeasurementHandler.removeMessages(MeasurementHandler.MSG_MEASURE);
|
|
||||||
mMeasurementHandler.sendEmptyMessage(MeasurementHandler.MSG_DISCONNECT);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void invalidate() {
|
|
||||||
mMeasurementHandler.sendEmptyMessage(MeasurementHandler.MSG_INVALIDATE);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class StatsObserver extends IPackageStatsObserver.Stub {
|
|
||||||
private final boolean mIsPrivate;
|
|
||||||
private final MeasurementDetails mDetails;
|
|
||||||
private final int mCurrentUser;
|
|
||||||
private final Message mFinished;
|
|
||||||
|
|
||||||
private int mRemaining;
|
|
||||||
|
|
||||||
public StatsObserver(boolean isPrivate, MeasurementDetails details, int currentUser,
|
|
||||||
Message finished, int remaining) {
|
|
||||||
mIsPrivate = isPrivate;
|
|
||||||
mDetails = details;
|
|
||||||
mCurrentUser = currentUser;
|
|
||||||
mFinished = finished;
|
|
||||||
mRemaining = remaining;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onGetStatsCompleted(PackageStats stats, boolean succeeded) {
|
|
||||||
synchronized (mDetails) {
|
|
||||||
if (succeeded) {
|
|
||||||
addStatsLocked(stats);
|
|
||||||
}
|
|
||||||
if (--mRemaining == 0) {
|
|
||||||
mFinished.sendToTarget();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addStatsLocked(PackageStats stats) {
|
|
||||||
if (mIsPrivate) {
|
|
||||||
long codeSize = stats.codeSize;
|
|
||||||
long dataSize = stats.dataSize;
|
|
||||||
long cacheSize = stats.cacheSize;
|
|
||||||
if (Environment.isExternalStorageEmulated()) {
|
|
||||||
// Include emulated storage when measuring internal. OBB is
|
|
||||||
// shared on emulated storage, so treat as code.
|
|
||||||
codeSize += stats.externalCodeSize + stats.externalObbSize;
|
|
||||||
dataSize += stats.externalDataSize + stats.externalMediaSize;
|
|
||||||
cacheSize += stats.externalCacheSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Count code and data for current user
|
|
||||||
if (stats.userHandle == mCurrentUser) {
|
|
||||||
mDetails.appsSize += codeSize;
|
|
||||||
mDetails.appsSize += dataSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
// User summary only includes data (code is only counted once
|
|
||||||
// for the current user)
|
|
||||||
addValue(mDetails.usersSize, stats.userHandle, dataSize);
|
|
||||||
|
|
||||||
// Include cache for all users
|
|
||||||
mDetails.cacheSize += cacheSize;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Physical storage; only count external sizes
|
|
||||||
mDetails.appsSize += stats.externalCodeSize + stats.externalDataSize
|
|
||||||
+ stats.externalMediaSize + stats.externalObbSize;
|
|
||||||
mDetails.cacheSize += stats.externalCacheSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class MainHandler extends Handler {
|
|
||||||
@Override
|
|
||||||
public void handleMessage(Message msg) {
|
|
||||||
final MeasurementDetails details = (MeasurementDetails) msg.obj;
|
|
||||||
final MeasurementReceiver receiver = (mReceiver != null) ? mReceiver.get() : null;
|
|
||||||
if (receiver != null) {
|
|
||||||
receiver.onDetailsChanged(details);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class MeasurementHandler extends Handler {
|
|
||||||
public static final int MSG_MEASURE = 1;
|
|
||||||
public static final int MSG_CONNECTED = 2;
|
|
||||||
public static final int MSG_DISCONNECT = 3;
|
|
||||||
public static final int MSG_COMPLETED = 4;
|
|
||||||
public static final int MSG_INVALIDATE = 5;
|
|
||||||
|
|
||||||
private Object mLock = new Object();
|
|
||||||
|
|
||||||
private IMediaContainerService mDefaultContainer;
|
|
||||||
|
|
||||||
private volatile boolean mBound = false;
|
|
||||||
|
|
||||||
private MeasurementDetails mCached;
|
|
||||||
|
|
||||||
private final ServiceConnection mDefContainerConn = new ServiceConnection() {
|
|
||||||
@Override
|
|
||||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
|
||||||
final IMediaContainerService imcs = IMediaContainerService.Stub.asInterface(
|
|
||||||
service);
|
|
||||||
mDefaultContainer = imcs;
|
|
||||||
mBound = true;
|
|
||||||
sendMessage(obtainMessage(MSG_CONNECTED, imcs));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onServiceDisconnected(ComponentName name) {
|
|
||||||
mBound = false;
|
|
||||||
removeMessages(MSG_CONNECTED);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public MeasurementHandler(Looper looper) {
|
|
||||||
super(looper);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleMessage(Message msg) {
|
|
||||||
switch (msg.what) {
|
|
||||||
case MSG_MEASURE: {
|
|
||||||
if (mCached != null) {
|
|
||||||
mMainHandler.obtainMessage(0, mCached).sendToTarget();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized (mLock) {
|
|
||||||
if (mBound) {
|
|
||||||
removeMessages(MSG_DISCONNECT);
|
|
||||||
sendMessage(obtainMessage(MSG_CONNECTED, mDefaultContainer));
|
|
||||||
} else {
|
|
||||||
Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
|
|
||||||
mContext.bindServiceAsUser(service, mDefContainerConn,
|
|
||||||
Context.BIND_AUTO_CREATE, UserHandle.OWNER);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case MSG_CONNECTED: {
|
|
||||||
final IMediaContainerService imcs = (IMediaContainerService) msg.obj;
|
|
||||||
measureExactStorage(imcs);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case MSG_DISCONNECT: {
|
|
||||||
synchronized (mLock) {
|
|
||||||
if (mBound) {
|
|
||||||
mBound = false;
|
|
||||||
mContext.unbindService(mDefContainerConn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case MSG_COMPLETED: {
|
|
||||||
mCached = (MeasurementDetails) msg.obj;
|
|
||||||
mMainHandler.obtainMessage(0, mCached).sendToTarget();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case MSG_INVALIDATE: {
|
|
||||||
mCached = null;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void measureExactStorage(IMediaContainerService imcs) {
|
|
||||||
final UserManager userManager = mContext.getSystemService(UserManager.class);
|
|
||||||
final PackageManager packageManager = mContext.getPackageManager();
|
|
||||||
|
|
||||||
final List<UserInfo> users = userManager.getUsers();
|
|
||||||
final int currentUser = ActivityManager.getCurrentUser();
|
|
||||||
|
|
||||||
final MeasurementDetails details = new MeasurementDetails();
|
|
||||||
final Message finished = mMeasurementHandler.obtainMessage(MeasurementHandler.MSG_COMPLETED,
|
|
||||||
details);
|
|
||||||
|
|
||||||
if (mSharedVolume != null && mSharedVolume.isMountedReadable()) {
|
|
||||||
final File basePath = mSharedVolume.getPathForUser(currentUser);
|
|
||||||
|
|
||||||
// Measure media types for emulated storage, or for primary physical
|
|
||||||
// external volume
|
|
||||||
for (String type : sMeasureMediaTypes) {
|
|
||||||
final File path = new File(basePath, type);
|
|
||||||
final long size = getDirectorySize(imcs, path);
|
|
||||||
details.mediaSize.put(type, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Measure misc files not counted under media
|
|
||||||
details.miscSize = measureMisc(imcs, basePath);
|
|
||||||
|
|
||||||
if (mSharedVolume.getType() == VolumeInfo.TYPE_EMULATED) {
|
|
||||||
// Measure total emulated storage of all users; internal apps data
|
|
||||||
// will be spliced in later
|
|
||||||
for (UserInfo user : users) {
|
|
||||||
final File userPath = mSharedVolume.getPathForUser(user.id);
|
|
||||||
final long size = getDirectorySize(imcs, userPath);
|
|
||||||
addValue(details.usersSize, user.id, size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Measure all apps hosted on this volume for all users
|
|
||||||
if (mVolume.getType() == VolumeInfo.TYPE_PRIVATE) {
|
|
||||||
final List<ApplicationInfo> apps = packageManager.getInstalledApplications(
|
|
||||||
PackageManager.GET_UNINSTALLED_PACKAGES
|
|
||||||
| PackageManager.GET_DISABLED_COMPONENTS);
|
|
||||||
|
|
||||||
final List<ApplicationInfo> volumeApps = new ArrayList<>();
|
|
||||||
for (ApplicationInfo app : apps) {
|
|
||||||
if (Objects.equals(app.volumeUuid, mVolume.getFsUuid())) {
|
|
||||||
volumeApps.add(app);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final int count = users.size() * volumeApps.size();
|
|
||||||
if (count == 0) {
|
|
||||||
finished.sendToTarget();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final StatsObserver observer = new StatsObserver(
|
|
||||||
true, details, currentUser, finished, count);
|
|
||||||
for (UserInfo user : users) {
|
|
||||||
for (ApplicationInfo app : volumeApps) {
|
|
||||||
packageManager.getPackageSizeInfo(app.packageName, user.id, observer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
finished.sendToTarget();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static long getDirectorySize(IMediaContainerService imcs, File path) {
|
|
||||||
try {
|
|
||||||
final long size = imcs.calculateDirectorySize(path.toString());
|
|
||||||
Log.d(TAG, "getDirectorySize(" + path + ") returned " + size);
|
|
||||||
return size;
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.w(TAG, "Could not read memory from default container service for " + path, e);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private long measureMisc(IMediaContainerService imcs, File dir) {
|
|
||||||
final File[] files = dir.listFiles();
|
|
||||||
if (ArrayUtils.isEmpty(files)) return 0;
|
|
||||||
|
|
||||||
// Get sizes of all top level nodes except the ones already computed
|
|
||||||
long miscSize = 0;
|
|
||||||
for (File file : files) {
|
|
||||||
final String name = file.getName();
|
|
||||||
if (sMeasureMediaTypes.contains(name)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (file.isFile()) {
|
|
||||||
miscSize += file.length();
|
|
||||||
} else if (file.isDirectory()) {
|
|
||||||
miscSize += getDirectorySize(imcs, file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return miscSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void addValue(SparseLongArray array, int key, long value) {
|
|
||||||
array.put(key, array.get(key) + value);
|
|
||||||
}
|
|
||||||
}
|
|
Reference in New Issue
Block a user