Move lagging users over to new storage API.
Over the last month we've been moving everyone over to the new StorageStatsManager public APIs, but we missed these users. The ApplicationsState changes are straightforward, but we had to completely rewrite StorageMeasurement to use the new fast-path quota APIs. Test: builds, boots, UI using StorageMeasurement works. Bug: 36056120 Change-Id: If02177c95bf8c96ae4eceac4d631a168f99bef84
This commit is contained in:
@@ -10703,7 +10703,7 @@ package android.content.pm {
|
||||
ctor public PackageManager.NameNotFoundException(java.lang.String);
|
||||
}
|
||||
|
||||
public class PackageStats implements android.os.Parcelable {
|
||||
public deprecated class PackageStats implements android.os.Parcelable {
|
||||
ctor public PackageStats(java.lang.String);
|
||||
ctor public PackageStats(android.os.Parcel);
|
||||
ctor public PackageStats(android.content.pm.PackageStats);
|
||||
|
||||
@@ -11423,7 +11423,7 @@ package android.content.pm {
|
||||
public static abstract class PackageManager.PermissionFlags implements java.lang.annotation.Annotation {
|
||||
}
|
||||
|
||||
public class PackageStats implements android.os.Parcelable {
|
||||
public deprecated class PackageStats implements android.os.Parcelable {
|
||||
ctor public PackageStats(java.lang.String);
|
||||
ctor public PackageStats(android.os.Parcel);
|
||||
ctor public PackageStats(android.content.pm.PackageStats);
|
||||
|
||||
@@ -10739,7 +10739,7 @@ package android.content.pm {
|
||||
ctor public PackageManager.NameNotFoundException(java.lang.String);
|
||||
}
|
||||
|
||||
public class PackageStats implements android.os.Parcelable {
|
||||
public deprecated class PackageStats implements android.os.Parcelable {
|
||||
ctor public PackageStats(java.lang.String);
|
||||
ctor public PackageStats(android.os.Parcel);
|
||||
ctor public PackageStats(android.content.pm.PackageStats);
|
||||
|
||||
@@ -34,6 +34,7 @@ import android.annotation.XmlRes;
|
||||
import android.app.PackageDeleteObserver;
|
||||
import android.app.PackageInstallObserver;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.app.usage.StorageStatsManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -5466,8 +5467,10 @@ public abstract class PackageManager {
|
||||
* the status of the operation. observer may be null to indicate that
|
||||
* no callback is desired.
|
||||
*
|
||||
* @deprecated use {@link StorageStatsManager} instead.
|
||||
* @hide
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract void getPackageSizeInfoAsUser(String packageName, @UserIdInt int userId,
|
||||
IPackageStatsObserver observer);
|
||||
|
||||
@@ -5475,8 +5478,10 @@ public abstract class PackageManager {
|
||||
* Like {@link #getPackageSizeInfoAsUser(String, int, IPackageStatsObserver)}, but
|
||||
* returns the size for the calling user.
|
||||
*
|
||||
* @deprecated use {@link StorageStatsManager} instead.
|
||||
* @hide
|
||||
*/
|
||||
@Deprecated
|
||||
public void getPackageSizeInfo(String packageName, IPackageStatsObserver observer) {
|
||||
getPackageSizeInfoAsUser(packageName, UserHandle.myUserId(), observer);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package android.content.pm;
|
||||
|
||||
import android.app.usage.StorageStatsManager;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.os.UserHandle;
|
||||
@@ -24,9 +25,13 @@ import android.text.TextUtils;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* implementation of PackageStats associated with a
|
||||
* application package.
|
||||
* implementation of PackageStats associated with a application package.
|
||||
*
|
||||
* @deprecated this class is an orphan that could never be obtained from a valid
|
||||
* public API. If you need package storage statistics use the new
|
||||
* {@link StorageStatsManager} APIs.
|
||||
*/
|
||||
@Deprecated
|
||||
public class PackageStats implements Parcelable {
|
||||
/** Name of the package to which this stats applies. */
|
||||
public String packageName;
|
||||
|
||||
@@ -19,6 +19,8 @@ package com.android.settingslib.applications;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.AppGlobals;
|
||||
import android.app.Application;
|
||||
import android.app.usage.StorageStats;
|
||||
import android.app.usage.StorageStatsManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -92,6 +94,7 @@ public class ApplicationsState {
|
||||
final PackageManager mPm;
|
||||
final IPackageManager mIpm;
|
||||
final UserManager mUm;
|
||||
final StorageStatsManager mStats;
|
||||
final int mAdminRetrieveFlags;
|
||||
final int mRetrieveFlags;
|
||||
PackageIntentReceiver mPackageIntentReceiver;
|
||||
@@ -111,6 +114,7 @@ public class ApplicationsState {
|
||||
final ArrayList<AppEntry> mAppEntries = new ArrayList<AppEntry>();
|
||||
List<ApplicationInfo> mApplications = new ArrayList<ApplicationInfo>();
|
||||
long mCurId = 1;
|
||||
String mCurComputingSizeUuid;
|
||||
String mCurComputingSizePkg;
|
||||
int mCurComputingSizeUserId;
|
||||
boolean mSessionsChanged;
|
||||
@@ -126,7 +130,8 @@ public class ApplicationsState {
|
||||
mContext = app;
|
||||
mPm = mContext.getPackageManager();
|
||||
mIpm = AppGlobals.getPackageManager();
|
||||
mUm = (UserManager) app.getSystemService(Context.USER_SERVICE);
|
||||
mUm = mContext.getSystemService(UserManager.class);
|
||||
mStats = mContext.getSystemService(StorageStatsManager.class);
|
||||
for (int userId : mUm.getProfileIdsWithDisabled(UserHandle.myUserId())) {
|
||||
mEntriesMap.put(userId, new HashMap<String, AppEntry>());
|
||||
}
|
||||
@@ -328,7 +333,18 @@ public class ApplicationsState {
|
||||
synchronized (mEntriesMap) {
|
||||
AppEntry entry = mEntriesMap.get(userId).get(packageName);
|
||||
if (entry != null) {
|
||||
mPm.getPackageSizeInfoAsUser(packageName, userId, mBackgroundHandler.mStatsObserver);
|
||||
mBackgroundHandler.post(() -> {
|
||||
final StorageStats stats = mStats.queryStatsForPackage(entry.info.volumeUuid,
|
||||
packageName, UserHandle.of(userId));
|
||||
final PackageStats legacyStats = new PackageStats(packageName, userId);
|
||||
legacyStats.codeSize = stats.getCodeBytes();
|
||||
legacyStats.dataSize = stats.getDataBytes();
|
||||
legacyStats.cacheSize = stats.getCacheBytes();
|
||||
try {
|
||||
mBackgroundHandler.mStatsObserver.onGetStatsCompleted(legacyStats, true);
|
||||
} catch (RemoteException ignored) {
|
||||
}
|
||||
});
|
||||
}
|
||||
if (DEBUG_LOCKING) Log.v(TAG, "...requestSize releasing lock");
|
||||
}
|
||||
@@ -958,10 +974,24 @@ public class ApplicationsState {
|
||||
mMainHandler.sendMessage(m);
|
||||
}
|
||||
entry.sizeLoadStart = now;
|
||||
mCurComputingSizeUuid = entry.info.volumeUuid;
|
||||
mCurComputingSizePkg = entry.info.packageName;
|
||||
mCurComputingSizeUserId = UserHandle.getUserId(entry.info.uid);
|
||||
mPm.getPackageSizeInfoAsUser(mCurComputingSizePkg,
|
||||
mCurComputingSizeUserId, mStatsObserver);
|
||||
|
||||
mBackgroundHandler.post(() -> {
|
||||
final StorageStats stats = mStats.queryStatsForPackage(
|
||||
mCurComputingSizeUuid, mCurComputingSizePkg,
|
||||
UserHandle.of(mCurComputingSizeUserId));
|
||||
final PackageStats legacyStats = new PackageStats(
|
||||
mCurComputingSizePkg, mCurComputingSizeUserId);
|
||||
legacyStats.codeSize = stats.getCodeBytes();
|
||||
legacyStats.dataSize = stats.getDataBytes();
|
||||
legacyStats.cacheSize = stats.getCacheBytes();
|
||||
try {
|
||||
mStatsObserver.onGetStatsCompleted(legacyStats, true);
|
||||
} catch (RemoteException ignored) {
|
||||
}
|
||||
});
|
||||
}
|
||||
if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing: now computing");
|
||||
return;
|
||||
|
||||
@@ -16,22 +16,14 @@
|
||||
|
||||
package com.android.settingslib.deviceinfo;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.content.ComponentName;
|
||||
import android.app.usage.ExternalStorageStats;
|
||||
import android.app.usage.StorageStats;
|
||||
import android.app.usage.StorageStatsManager;
|
||||
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.AsyncTask;
|
||||
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.SystemClock;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.os.storage.StorageVolume;
|
||||
@@ -40,93 +32,54 @@ import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
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}.
|
||||
* {@link StorageVolume}.
|
||||
*/
|
||||
public class StorageMeasurement {
|
||||
private static final String TAG = "StorageMeasurement";
|
||||
|
||||
private static final boolean LOCAL_LOGV = false;
|
||||
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 {
|
||||
/** Size of storage device. */
|
||||
public long totalSize;
|
||||
/** Size of available space. */
|
||||
public long availSize;
|
||||
/** Size of all cached data. */
|
||||
public long cacheSize;
|
||||
|
||||
/**
|
||||
* Total apps disk usage per profiles of the current user.
|
||||
* Total disk space used by everything.
|
||||
* <p>
|
||||
* When measuring internal storage, this value includes the code size of
|
||||
* all apps (regardless of install status for the given profile), and
|
||||
* internal disk used by the profile's apps. When the device
|
||||
* emulates external storage, this value also includes emulated storage
|
||||
* used by the profile's apps.
|
||||
* <p>
|
||||
* When measuring a physical {@link StorageVolume}, this value includes
|
||||
* usage by all apps on that volume and only for the primary profile.
|
||||
* Key is {@link UserHandle}.
|
||||
*/
|
||||
public SparseLongArray usersSize = new SparseLongArray();
|
||||
|
||||
/**
|
||||
* Total disk space used by apps.
|
||||
* <p>
|
||||
* Key is {@link UserHandle}.
|
||||
*/
|
||||
public SparseLongArray appsSize = new SparseLongArray();
|
||||
|
||||
/**
|
||||
* Total cache disk usage by apps (over all users and profiles).
|
||||
*/
|
||||
public long cacheSize;
|
||||
|
||||
/**
|
||||
* Total media disk usage, categorized by types such as
|
||||
* {@link Environment#DIRECTORY_MUSIC} for every user profile of the current user.
|
||||
* Total disk space used by media on shared storage.
|
||||
* <p>
|
||||
* When measuring internal storage, this reflects media on emulated
|
||||
* storage for the respective profile.
|
||||
* <p>
|
||||
* When measuring a physical {@link StorageVolume}, this reflects media
|
||||
* on that volume.
|
||||
* <p>
|
||||
* Key of the {@link SparseArray} is {@link UserHandle}.
|
||||
* First key is {@link UserHandle}. Second key is media type, such as
|
||||
* {@link Environment#DIRECTORY_PICTURES}.
|
||||
*/
|
||||
public SparseArray<HashMap<String, Long>> mediaSize = new SparseArray<>();
|
||||
|
||||
/**
|
||||
* Misc external disk usage for the current user's profiles, unaccounted in
|
||||
* {@link #mediaSize}. Key is {@link UserHandle}.
|
||||
* Total disk space used by non-media on shared storage.
|
||||
* <p>
|
||||
* Key is {@link UserHandle}.
|
||||
*/
|
||||
public SparseLongArray miscSize = new SparseLongArray();
|
||||
|
||||
/**
|
||||
* Total disk usage for users, which is only meaningful for emulated
|
||||
* internal storage. Key is {@link UserHandle}.
|
||||
*/
|
||||
public SparseLongArray usersSize = new SparseLongArray();
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MeasurementDetails: [totalSize: " + totalSize + " availSize: " + availSize
|
||||
@@ -142,25 +95,19 @@ public class StorageMeasurement {
|
||||
private WeakReference<MeasurementReceiver> mReceiver;
|
||||
|
||||
private final Context mContext;
|
||||
private final UserManager mUser;
|
||||
private final StorageStatsManager mStats;
|
||||
|
||||
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();
|
||||
mUser = mContext.getSystemService(UserManager.class);
|
||||
mStats = mContext.getSystemService(StorageStatsManager.class);
|
||||
|
||||
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) {
|
||||
@@ -170,315 +117,94 @@ public class StorageMeasurement {
|
||||
}
|
||||
|
||||
public void forceMeasure() {
|
||||
invalidate();
|
||||
measure();
|
||||
}
|
||||
|
||||
public void measure() {
|
||||
if (!mMeasurementHandler.hasMessages(MeasurementHandler.MSG_MEASURE)) {
|
||||
mMeasurementHandler.sendEmptyMessage(MeasurementHandler.MSG_MEASURE);
|
||||
}
|
||||
new MeasureTask().execute();
|
||||
}
|
||||
|
||||
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,
|
||||
List<UserInfo> profiles, Message finished, int remaining) {
|
||||
mIsPrivate = isPrivate;
|
||||
mDetails = details;
|
||||
mCurrentUser = currentUser;
|
||||
if (isPrivate) {
|
||||
// Add the profile ids as keys to detail's app sizes.
|
||||
for (UserInfo userInfo : profiles) {
|
||||
mDetails.appsSize.put(userInfo.id, 0);
|
||||
}
|
||||
}
|
||||
mFinished = finished;
|
||||
mRemaining = remaining;
|
||||
private class MeasureTask extends AsyncTask<Void, Void, MeasurementDetails> {
|
||||
@Override
|
||||
protected MeasurementDetails doInBackground(Void... params) {
|
||||
return measureExactStorage();
|
||||
}
|
||||
|
||||
@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's profiles (keys prepared in constructor)
|
||||
addValueIfKeyExists(mDetails.appsSize, stats.userHandle, codeSize + 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
|
||||
addValue(mDetails.appsSize, mCurrentUser,
|
||||
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;
|
||||
protected void onPostExecute(MeasurementDetails result) {
|
||||
final MeasurementReceiver receiver = (mReceiver != null) ? mReceiver.get() : null;
|
||||
if (receiver != null) {
|
||||
receiver.onDetailsChanged(details);
|
||||
receiver.onDetailsChanged(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 MeasurementDetails measureExactStorage() {
|
||||
final List<UserInfo> users = mUser.getUsers();
|
||||
|
||||
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.SYSTEM);
|
||||
}
|
||||
}
|
||||
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 List<UserInfo> currentProfiles = userManager.getEnabledProfiles(
|
||||
ActivityManager.getCurrentUser());
|
||||
final long start = SystemClock.elapsedRealtime();
|
||||
|
||||
final MeasurementDetails details = new MeasurementDetails();
|
||||
final Message finished = mMeasurementHandler.obtainMessage(MeasurementHandler.MSG_COMPLETED,
|
||||
details);
|
||||
if (mVolume == null) return details;
|
||||
|
||||
if (mVolume == null || !mVolume.isMountedReadable()) {
|
||||
finished.sendToTarget();
|
||||
return;
|
||||
}
|
||||
details.totalSize = mStats.getTotalBytes(mVolume.fsUuid);
|
||||
details.availSize = mStats.getFreeBytes(mVolume.fsUuid);
|
||||
|
||||
final long finishTotal = SystemClock.elapsedRealtime();
|
||||
Log.d(TAG, "Measured total storage in " + (finishTotal - start) + "ms");
|
||||
|
||||
if (mSharedVolume != null && mSharedVolume.isMountedReadable()) {
|
||||
for (UserInfo currentUserInfo : currentProfiles) {
|
||||
final int userId = currentUserInfo.id;
|
||||
final File basePath = mSharedVolume.getPathForUser(userId);
|
||||
HashMap<String, Long> mediaMap = new HashMap<>(sMeasureMediaTypes.size());
|
||||
details.mediaSize.put(userId, mediaMap);
|
||||
|
||||
// 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);
|
||||
mediaMap.put(type, size);
|
||||
}
|
||||
|
||||
// Measure misc files not counted under media
|
||||
addValue(details.miscSize, userId, 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final File file = mVolume.getPath();
|
||||
if (file != null) {
|
||||
details.totalSize = file.getTotalSpace();
|
||||
details.availSize = file.getFreeSpace();
|
||||
}
|
||||
|
||||
// Measure all apps hosted on this volume for all users
|
||||
if (mVolume.getType() == VolumeInfo.TYPE_PRIVATE) {
|
||||
final List<ApplicationInfo> apps = packageManager.getInstalledApplications(
|
||||
PackageManager.MATCH_ANY_USER
|
||||
| PackageManager.MATCH_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,
|
||||
ActivityManager.getCurrentUser(), currentProfiles, finished, count);
|
||||
for (UserInfo user : users) {
|
||||
for (ApplicationInfo app : volumeApps) {
|
||||
packageManager.getPackageSizeInfoAsUser(app.packageName, user.id, observer);
|
||||
final HashMap<String, Long> mediaMap = new HashMap<>();
|
||||
details.mediaSize.put(user.id, mediaMap);
|
||||
|
||||
final ExternalStorageStats stats = mStats
|
||||
.queryExternalStatsForUser(mSharedVolume.fsUuid, UserHandle.of(user.id));
|
||||
|
||||
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 = mStats.queryStatsForUser(mVolume.fsUuid,
|
||||
UserHandle.of(user.id));
|
||||
|
||||
// Only count code once against current user
|
||||
if (user.id == UserHandle.myUserId()) {
|
||||
addValue(details.usersSize, user.id, stats.getCodeBytes());
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
finished.sendToTarget();
|
||||
return;
|
||||
}
|
||||
}
|
||||
addValue(details.usersSize, user.id, stats.getDataBytes());
|
||||
addValue(details.appsSize, user.id, stats.getCodeBytes() + stats.getDataBytes());
|
||||
|
||||
private static long getDirectorySize(IMediaContainerService imcs, File path) {
|
||||
try {
|
||||
final long size = imcs.calculateDirectorySize(path.toString());
|
||||
if (LOGV) Log.v(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);
|
||||
details.cacheSize += stats.getCacheBytes();
|
||||
}
|
||||
}
|
||||
return miscSize;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
private static void addValueIfKeyExists(SparseLongArray array, int key, long value) {
|
||||
final int index = array.indexOfKey(key);
|
||||
if (index >= 0) {
|
||||
array.put(key, array.valueAt(index) + value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user