Merge "Add queryStatsForPackage() API."

This commit is contained in:
Jeff Sharkey
2017-02-23 03:21:42 +00:00
committed by Android (Google) Code Review
8 changed files with 111 additions and 3 deletions

View File

@@ -6882,6 +6882,7 @@ package android.app.usage {
method public long getFreeBytes(java.lang.String);
method public long getTotalBytes(java.lang.String);
method public android.app.usage.ExternalStorageStats queryExternalStatsForUser(java.lang.String, android.os.UserHandle);
method public android.app.usage.StorageStats queryStatsForPackage(java.lang.String, java.lang.String, android.os.UserHandle);
method public android.app.usage.StorageStats queryStatsForUid(java.lang.String, int);
method public android.app.usage.StorageStats queryStatsForUser(java.lang.String, android.os.UserHandle);
}

View File

@@ -7304,6 +7304,7 @@ package android.app.usage {
method public long getFreeBytes(java.lang.String);
method public long getTotalBytes(java.lang.String);
method public android.app.usage.ExternalStorageStats queryExternalStatsForUser(java.lang.String, android.os.UserHandle);
method public android.app.usage.StorageStats queryStatsForPackage(java.lang.String, java.lang.String, android.os.UserHandle);
method public android.app.usage.StorageStats queryStatsForUid(java.lang.String, int);
method public android.app.usage.StorageStats queryStatsForUser(java.lang.String, android.os.UserHandle);
}

View File

@@ -6908,6 +6908,7 @@ package android.app.usage {
method public long getFreeBytes(java.lang.String);
method public long getTotalBytes(java.lang.String);
method public android.app.usage.ExternalStorageStats queryExternalStatsForUser(java.lang.String, android.os.UserHandle);
method public android.app.usage.StorageStats queryStatsForPackage(java.lang.String, java.lang.String, android.os.UserHandle);
method public android.app.usage.StorageStats queryStatsForUid(java.lang.String, int);
method public android.app.usage.StorageStats queryStatsForUser(java.lang.String, android.os.UserHandle);
}

View File

@@ -24,6 +24,7 @@ interface IStorageStatsManager {
boolean isQuotaSupported(String volumeUuid, String callingPackage);
long getTotalBytes(String volumeUuid, String callingPackage);
long getFreeBytes(String volumeUuid, String callingPackage);
StorageStats queryStatsForPackage(String volumeUuid, String packageName, int userId, String callingPackage);
StorageStats queryStatsForUid(String volumeUuid, int uid, String callingPackage);
StorageStats queryStatsForUser(String volumeUuid, int userId, String callingPackage);
ExternalStorageStats queryExternalStatsForUser(String volumeUuid, int userId, String callingPackage);

View File

@@ -19,6 +19,7 @@ package android.app.usage;
import android.annotation.WorkerThread;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -99,6 +100,37 @@ public class StorageStatsManager {
}
}
/**
* Return storage statistics for a specific package on the requested storage
* volume.
* <p>
* This method may take several seconds to calculate the requested values,
* so it should only be called from a worker thread.
* <p class="note">
* Note: if the requested package uses the {@code android:sharedUserId}
* manifest feature, this call will be forced into a slower manual
* calculation path. If possible, consider always using
* {@link #queryStatsForUid(String, int)}, which is typically faster.
* </p>
*
* @param volumeUuid the UUID of the storage volume you're interested in, or
* {@code null} to specify the default internal storage.
* @param packageName the package name you're interested in.
* @param user the user you're interested in.
* @see ApplicationInfo#volumeUuid
* @see PackageInfo#packageName
*/
@WorkerThread
public StorageStats queryStatsForPackage(String volumeUuid, String packageName,
UserHandle user) {
try {
return mService.queryStatsForPackage(volumeUuid, packageName, user.getIdentifier(),
mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Return storage statistics for a specific UID on the requested storage
* volume.

View File

@@ -770,4 +770,17 @@ public class FileUtils {
return dir.mkdir() ? dir : null;
}
/**
* Round the given size of a storage device to a nice round power-of-two
* value, such as 256MB or 32GB. This avoids showing weird values like
* "29.5GB" in UI.
*/
public static long roundStorageSize(long size) {
long res = 1;
while (res < size) {
res <<= 1;
}
return res;
}
}

View File

@@ -311,6 +311,28 @@ public class FileUtilsTest extends AndroidTestCase {
assertNameEquals("test.foo (1).bar", FileUtils.buildUniqueFile(mTarget, "test.foo.bar"));
}
public void testRoundStorageSize() throws Exception {
final long M128 = 134217728L;
final long M256 = M128 * 2;
final long M512 = M256 * 2;
final long M1024 = M512 * 2;
final long G16 = M1024 * 16;
final long G32 = M1024 * 32;
final long G64 = M1024 * 64;
assertEquals(M128, FileUtils.roundStorageSize(M128));
assertEquals(M256, FileUtils.roundStorageSize(M128 + 1));
assertEquals(M256, FileUtils.roundStorageSize(M256 - 1));
assertEquals(M256, FileUtils.roundStorageSize(M256));
assertEquals(M512, FileUtils.roundStorageSize(M256 + 1));
assertEquals(G16, FileUtils.roundStorageSize(G16));
assertEquals(G32, FileUtils.roundStorageSize(G16 + 1));
assertEquals(G32, FileUtils.roundStorageSize(G32 - 1));
assertEquals(G32, FileUtils.roundStorageSize(G32));
assertEquals(G64, FileUtils.roundStorageSize(G32 + 1));
}
private static void assertNameEquals(String expected, File actual) {
assertEquals(expected, actual.getName());
}

View File

@@ -30,6 +30,7 @@ import android.content.pm.PackageStats;
import android.content.pm.UserInfo;
import android.os.Binder;
import android.os.Environment;
import android.os.FileUtils;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -148,11 +149,10 @@ public class StorageStatsService extends IStorageStatsManager.Stub {
enforcePermission(Binder.getCallingUid(), callingPackage);
if (volumeUuid == StorageManager.UUID_PRIVATE_INTERNAL) {
// TODO: round total size to nearest power of two
return mStorage.getPrimaryStorageSize();
return FileUtils.roundStorageSize(mStorage.getPrimaryStorageSize());
} else {
final VolumeInfo vol = mStorage.findVolumeByUuid(volumeUuid);
return vol.disk.size;
return FileUtils.roundStorageSize(vol.disk.size);
}
}
@@ -174,6 +174,43 @@ public class StorageStatsService extends IStorageStatsManager.Stub {
}
}
@Override
public StorageStats queryStatsForPackage(String volumeUuid, String packageName, int userId,
String callingPackage) {
enforcePermission(Binder.getCallingUid(), callingPackage);
if (userId != UserHandle.getCallingUserId()) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS, TAG);
}
final ApplicationInfo appInfo;
try {
appInfo = mPackage.getApplicationInfoAsUser(packageName, 0, userId);
} catch (NameNotFoundException e) {
throw new IllegalStateException(e);
}
if (mPackage.getPackagesForUid(appInfo.uid).length == 1) {
// Only one package inside UID means we can fast-path
return queryStatsForUid(volumeUuid, appInfo.uid, callingPackage);
} else {
// Multiple packages means we need to go manual
final int appId = UserHandle.getUserId(appInfo.uid);
final String[] packageNames = new String[] { packageName };
final long[] ceDataInodes = new long[1];
final String[] codePaths = new String[] { appInfo.getCodePath() };
final PackageStats stats = new PackageStats(TAG);
try {
mInstaller.getAppSize(volumeUuid, packageNames, userId, 0,
appId, ceDataInodes, codePaths, stats);
} catch (InstallerException e) {
throw new IllegalStateException(e);
}
return translate(stats);
}
}
@Override
public StorageStats queryStatsForUid(String volumeUuid, int uid, String callingPackage) {
enforcePermission(Binder.getCallingUid(), callingPackage);