如何使用StorageStatsManager。queryStatsForPackage在Android上啊?

时间:2022-02-23 16:49:15

Background

Before Android O, in order to get an app size, you had to have a special permission for it, and use a hidden API.

在Android O之前,为了获得一个应用程序的大小,你必须得到一个特殊的许可,并且使用一个隐藏的API。

The problem

Such a solution won't work anymore, but instead, Google provides an official API that's less granular: queryStatsForPackage , which returns StorageStats object.

这样的解决方案不再有效,但是,谷歌提供了一个官方API,它的粒度更小:queryStatsForPackage,它返回StorageStats对象。

However, I can't find any information of how to use it.

然而,我找不到任何关于如何使用它的信息。

As opposed to the hidden API, which only required the package name of the app, this one also requires "String volumeUuid" and "UserHandle user".

与只需要应用程序包名的隐藏API不同,这个API还需要“String volumeUuid”和“UserHandle user”。

The questions

  1. How do I provide those parameters?
  2. 我如何提供这些参数?
  3. What do they mean? Is each of the volumeUuid refer to a different storage (build in storage and real sd-card, for example), and UserHandle is for each user?
  4. 他们是什么意思?每个卷都引用不同的存储(例如,在存储和实际的sd卡中构建),而UserHandle是针对每个用户的吗?
  5. If #2 is correct, should I really call each of them for each possible value, to get the real total storage the app takes? If so, how?
  6. 如果#2是正确的,我是否应该为每个可能的值调用它们,以得到应用程序的实际总存储?如果是这样,如何?

3 个解决方案

#1


3  

OK, I've found out how to use the new API. I've prepared a small sample of this, fetching some information about the volume storage and of a specific app (this time of the Play Store, but you can change it if needed) :

好的,我已经知道如何使用新的API了。我已经准备了一个小样本,获取关于容量存储和特定应用程序的一些信息(在Play Store的这段时间,但是如果需要,您可以更改它):

public class MainActivity extends AppCompatActivity {

    public static final String PACKAGE_NAME = "com.android.vending";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (!hasUsageStatsPermission(this))
            startActivityForResult(new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS).addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY), 1);
        else {
            final Context context = this;
            AsyncTask.execute(new Runnable() {
                @TargetApi(VERSION_CODES.O)
                @Override
                public void run() {
                    @SuppressLint("WrongConstant") final StorageStatsManager storageStatsManager = (StorageStatsManager) getSystemService(Context.STORAGE_STATS_SERVICE);
                    final StorageManager storageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
                    final List<StorageVolume> storageVolumes = storageManager.getStorageVolumes();
                    final UserHandle user = Process.myUserHandle();
                    for (StorageVolume storageVolume : storageVolumes) {
                        final String uuidStr = storageVolume.getUuid();
                        final UUID uuid = uuidStr == null ? StorageManager.UUID_DEFAULT : UUID.fromString(uuidStr);
                        try {
                            Log.d("AppLog", "storage:" + uuid + " : " + storageVolume.getDescription(context) + " : " + storageVolume.getState());
                            Log.d("AppLog", "getFreeBytes:" + Formatter.formatShortFileSize(context, storageStatsManager.getFreeBytes(uuid)));
                            Log.d("AppLog", "getTotalBytes:" + Formatter.formatShortFileSize(context, storageStatsManager.getTotalBytes(uuid)));
                            Log.d("AppLog", "storage stats for app of package name:" + PACKAGE_NAME + " : ");

                            final StorageStats storageStats = storageStatsManager.queryStatsForPackage(uuid, PACKAGE_NAME, user);
                            Log.d("AppLog", "getAppBytes:" + Formatter.formatShortFileSize(context, storageStats.getAppBytes()) +
                                    " getCacheBytes:" + Formatter.formatShortFileSize(context, storageStats.getCacheBytes()) +
                                    " getDataBytes:" + Formatter.formatShortFileSize(context, storageStats.getDataBytes()));
                        } catch (NameNotFoundException e) {
                            e.printStackTrace();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            });

        }
    }

    @TargetApi(VERSION_CODES.M)
    public static boolean hasUsageStatsPermission(Context context) {
        //http://*.com/a/42390614/878126
        if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP)
            return false;
        AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
        final int mode = appOps.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, android.os.Process.myUid(), context.getPackageName());
        boolean granted = false;
        if (mode == AppOpsManager.MODE_DEFAULT)
            granted = (context.checkCallingOrSelfPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) == PackageManager.PERMISSION_GRANTED);
        else
            granted = (mode == AppOpsManager.MODE_ALLOWED);
        return granted;
    }
}

manifest file should have this:

清单文件应该包含以下内容:

<uses-permission
    android:name="android.permission.PACKAGE_USAGE_STATS"
    tools:ignore="ProtectedPermissions"/>

#2


0  

Pass null as the VolumeUuid, to get values for the default internal storage, or get specific volumes via the StorageManager system service.

将null传递为VolumeUuid,以获取默认内部存储的值,或者通过StorageManager系统服务获取特定的卷。

You can get your own UserHandle via Process.myUserHandle(). Other users of the system occur when you give someone a guest login on your device, or when there is an Android for Work profile installed on the device. I don't think there is a simple way to enumerate all the users that have access to the device.

您可以通过Process.myUserHandle()获得自己的用户句柄。当你在你的设备上给某人一个访客登录,或者在设备上安装了一个工作概要文件时,系统的其他用户就会出现。我认为没有一种简单的方法来枚举所有访问该设备的用户。

#3


0  

An alternative approach for getting UUID.

获得UUID的另一种方法。

To get UUIDs, you can also use StorageManager.getUuidForPath(File path) function. As to the path, use Context.getFilesDir() and Context.getExternalFilesDirs() to get all possibles directories that an app data will be saved to.

要获得uuid,还可以使用StorageManager。getUuidForPath(文件路径)的功能。至于路径,请使用Context.getFilesDir()和Context.getExternalFilesDirs()来获取应用程序数据将被保存到的所有可能目录。

Please note that different path may return the same UUID, so you have to use a hash set to collect unique UUIDs

请注意,不同的路径可能返回相同的UUID,因此您必须使用一个散列集来收集惟一的UUID。

#1


3  

OK, I've found out how to use the new API. I've prepared a small sample of this, fetching some information about the volume storage and of a specific app (this time of the Play Store, but you can change it if needed) :

好的,我已经知道如何使用新的API了。我已经准备了一个小样本,获取关于容量存储和特定应用程序的一些信息(在Play Store的这段时间,但是如果需要,您可以更改它):

public class MainActivity extends AppCompatActivity {

    public static final String PACKAGE_NAME = "com.android.vending";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (!hasUsageStatsPermission(this))
            startActivityForResult(new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS).addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY), 1);
        else {
            final Context context = this;
            AsyncTask.execute(new Runnable() {
                @TargetApi(VERSION_CODES.O)
                @Override
                public void run() {
                    @SuppressLint("WrongConstant") final StorageStatsManager storageStatsManager = (StorageStatsManager) getSystemService(Context.STORAGE_STATS_SERVICE);
                    final StorageManager storageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
                    final List<StorageVolume> storageVolumes = storageManager.getStorageVolumes();
                    final UserHandle user = Process.myUserHandle();
                    for (StorageVolume storageVolume : storageVolumes) {
                        final String uuidStr = storageVolume.getUuid();
                        final UUID uuid = uuidStr == null ? StorageManager.UUID_DEFAULT : UUID.fromString(uuidStr);
                        try {
                            Log.d("AppLog", "storage:" + uuid + " : " + storageVolume.getDescription(context) + " : " + storageVolume.getState());
                            Log.d("AppLog", "getFreeBytes:" + Formatter.formatShortFileSize(context, storageStatsManager.getFreeBytes(uuid)));
                            Log.d("AppLog", "getTotalBytes:" + Formatter.formatShortFileSize(context, storageStatsManager.getTotalBytes(uuid)));
                            Log.d("AppLog", "storage stats for app of package name:" + PACKAGE_NAME + " : ");

                            final StorageStats storageStats = storageStatsManager.queryStatsForPackage(uuid, PACKAGE_NAME, user);
                            Log.d("AppLog", "getAppBytes:" + Formatter.formatShortFileSize(context, storageStats.getAppBytes()) +
                                    " getCacheBytes:" + Formatter.formatShortFileSize(context, storageStats.getCacheBytes()) +
                                    " getDataBytes:" + Formatter.formatShortFileSize(context, storageStats.getDataBytes()));
                        } catch (NameNotFoundException e) {
                            e.printStackTrace();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            });

        }
    }

    @TargetApi(VERSION_CODES.M)
    public static boolean hasUsageStatsPermission(Context context) {
        //http://*.com/a/42390614/878126
        if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP)
            return false;
        AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
        final int mode = appOps.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, android.os.Process.myUid(), context.getPackageName());
        boolean granted = false;
        if (mode == AppOpsManager.MODE_DEFAULT)
            granted = (context.checkCallingOrSelfPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) == PackageManager.PERMISSION_GRANTED);
        else
            granted = (mode == AppOpsManager.MODE_ALLOWED);
        return granted;
    }
}

manifest file should have this:

清单文件应该包含以下内容:

<uses-permission
    android:name="android.permission.PACKAGE_USAGE_STATS"
    tools:ignore="ProtectedPermissions"/>

#2


0  

Pass null as the VolumeUuid, to get values for the default internal storage, or get specific volumes via the StorageManager system service.

将null传递为VolumeUuid,以获取默认内部存储的值,或者通过StorageManager系统服务获取特定的卷。

You can get your own UserHandle via Process.myUserHandle(). Other users of the system occur when you give someone a guest login on your device, or when there is an Android for Work profile installed on the device. I don't think there is a simple way to enumerate all the users that have access to the device.

您可以通过Process.myUserHandle()获得自己的用户句柄。当你在你的设备上给某人一个访客登录,或者在设备上安装了一个工作概要文件时,系统的其他用户就会出现。我认为没有一种简单的方法来枚举所有访问该设备的用户。

#3


0  

An alternative approach for getting UUID.

获得UUID的另一种方法。

To get UUIDs, you can also use StorageManager.getUuidForPath(File path) function. As to the path, use Context.getFilesDir() and Context.getExternalFilesDirs() to get all possibles directories that an app data will be saved to.

要获得uuid,还可以使用StorageManager。getUuidForPath(文件路径)的功能。至于路径,请使用Context.getFilesDir()和Context.getExternalFilesDirs()来获取应用程序数据将被保存到的所有可能目录。

Please note that different path may return the same UUID, so you have to use a hash set to collect unique UUIDs

请注意,不同的路径可能返回相同的UUID,因此您必须使用一个散列集来收集惟一的UUID。