HOME > android > tips

안드로이드 - StorageStatsManager로 App 크기 계산하기 (StorageStats)

By JS | 06 Oct 2018

Android의 StorageStatsManagerService는 Storage stats에 대한 정보를 제공해주는 Service입니다. 이를 이용하여 App이 디바이스에서 차지하는 size를 계산할 때 사용할 수 있습니다. App뿐만이 아니라 UID, User 별로 차지하는 정보도 알 수 있습니다.

이 Service는 PACKAGE_USAGE_STATS 권한을 요구합니다.

<permission android:name="android.permission.PACKAGE_USAGE_STATS"
    android:protectionLevel="signature|privileged|development|appop" />
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />

protectionLevel이 "signature|privileged|appop" 이기 때문에 일반 앱들이 이 권한을 얻으려면 사용자가 세팅앱에 들어가서 앱에게 권한을 부여해줘야 합니다.

권한 요청

먼저 프로젝트를 만들고 AndroidManifest.xml에 permission을 선언합니다.

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

그리고 앱에서 다음 인텐트를 실행하면 이 권한을 부여할 수 있는 세팅앱의 액티비티가 실행됩니다.

Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
startActivity(intent);

나의 앱을 선택하고 기능을 활성해해주면 권한을 받을 수 있습니다.

ADB로 권한 부여하기

만약 테스트 목적이라면 adb로 이 권한을 앱에 부여할 수 있습니다. 앱을 설치하고, 다음 adb 명령어를 입력하면 com.codechacha.storagestatssample앱에 PACKAGE_USAGE_STATS권한을 줄 수 있습니다.

$ adb shell pm grant com.codechacha.storagestatssample android.permission.PACKAGE_USAGE_STATS

권한이 잘 설정되었는지는 다음 명령어로 간단히 확인해 볼 수 있습니다. install permissions에 우리가 추가한 권한이 granted=true로 되어있어야 합니다.

$ adb shell dumpsys package com.codechacha.storagestatssample

Package [com.codechacha.storagestatssample] (ff5c605):
...
requested permissions:
  android.permission.PACKAGE_USAGE_STATS
install permissions:
  android.permission.PACKAGE_USAGE_STATS: granted=true
...

특정 Package(App)의 Storage stats 얻기

MainActivity.java에 아래 코드를 입력합니다. queryIntentActivities()로 Launcher에 보여지는 앱 정보들을 가져왔고 StorageStats.queryStatsForPackage(UUID, packageName, UserHandle)로 각각의 앱에 대한 stats를 얻었습니다.

UUID는 앱이 설치된 Storage의 unique id입니다. 앱이 설치된 Storage의 UUID는 ResolveInfo에서 얻을 수 있습니다. UserHandle은 설치된 User의 handle입니다. 현재 앱이 실행중인 UserHandle을 가져왔습니다.

private void getStorageStatsForPackage() {
    StorageStatsManager statsManager = getSystemService(StorageStatsManager.class);
    Intent intent = new Intent(Intent.ACTION_MAIN);
    intent.addCategory(Intent.CATEGORY_LAUNCHER);
    List<ResolveInfo> ris = getPackageManager().queryIntentActivities(intent, 0);
    Log.d(TAG, "getStorageStatsForPackage");
    if (ris != null) {
        for (ResolveInfo ri : ris) {
            UUID uuid = ri.activityInfo.applicationInfo.storageUuid;
            String packageName = ri.activityInfo.packageName;
            UserHandle user = android.os.Process.myUserHandle();
            Log.d(TAG, "packageName: " + packageName);
            try {
                StorageStats stats = statsManager.queryStatsForPackage(
                        uuid, packageName, user);
                Log.d(TAG, "App size: " + stats.getAppBytes() +
                        ", Cache size: " + stats.getCacheBytes() +
                        ", Data size: " + stats.getDataBytes());
            } catch (IOException e) {
                e.printStackTrace();
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
        }
    }
}

로그로 결과를 보면 아래처럼 출력이 됩니다.

10-08 23:32:34.617  4660  4660 D MainActivity: App size: 4096, Cache size: 16384, Data size: 36864
10-08 23:32:34.617  4660  4660 D MainActivity: packageName: com.android.deskclock
10-08 23:32:34.617  4660  4660 D MainActivity: App size: 4096, Cache size: 16384, Data size: 126976
10-08 23:32:34.617  4660  4660 D MainActivity: packageName: com.android.settings
10-08 23:32:34.618  4660  4660 D MainActivity: App size: 21114880, Cache size: 16384, Data size: 143360
10-08 23:32:34.620  4660  4660 D MainActivity: packageName: com.google.android.dialer
10-08 23:32:34.620  4660  4660 D MainActivity: App size: 389120, Cache size: 77824, Data size: 143360

Android developer를 보면 StorageStas API에 대한 설명이 나옵니다.

간단히 정리하면...

  1. getAppBytes() Apk, 최적화된 dex 파일, native library 등을 포함하는 App의 size를 리턴합니다.

  2. getCacheBytes() 모든 Cached data의 size를 리턴합니다. Context.getCacheDir()와 Context.getCodeCacheDir()에 있는 경로의 파일들의 크기를 계산합니다.

  3. getDataBytes() 모든 data의 size를 리턴합니다. Context.getDataDir(), Context.getCacheDir(), Context.getCodeCacheDir()들의 경로에 있는 파일들을 크기를 계산합니다.

입니다. 여기서 cache와 data는 겹쳐지는 영역이 있습니다. data가 cache를 포함하고 있습니다. 만약 전체 크기를 구하려면 cache를 제외한 App과 Data영역을 더하면 됩니다.

특정 UID의 Storage stats 얻기

UID는 UserId를 말합니다. 일반적으로 1개의 앱은 1개의 UID로 구성되어있습니다. 하지만 SharedUID는 여러 앱이 1개의 UID를 공유할 수 있어서, 1개의 UID를 사용하는 앱이 5개가 될 수 있습니다.

여기서는 특정 UID를 사용하는 모든 앱의 size를 구합니다. 어떤 UID를 사용하는 앱이 디바이스에 5개가 설치되어있다면 5개의 앱에 대한 size를 구합니다.

아래 코드처럼 입력합니다. queryStatsForUid(UUID, UID)를 입력하면 UID에 대한 StorageStats를 리턴해줍니다. UUID는 Default를 입력했습니다. Default는 단말의 내장 Storage를 의미합니다.

private void getStorageStatsForUid() {
    StorageStatsManager statsManager = getSystemService(StorageStatsManager.class);
    int systemUid = 1000;
    try {
        StorageStats stats = statsManager.queryStatsForUid(
                UUID_DEFAULT, systemUid);
        Log.d(TAG, "[System UID] App size: " + stats.getAppBytes() +
                ", Cache size: " + stats.getCacheBytes() +
                ", Data size: " + stats.getDataBytes());
    } catch (IOException e) {
        e.printStackTrace();
    }
}

결과를 보면 입력한 UID를 사용하는 앱들의 전체 size를 알 수 있습니다.

10-08 23:33:47.939  4861  4861 D MainActivity: [System UID] App size: 32768, Cache size: 131072, Data size: 438272

특정 User의 Storage stats 얻기

User는 사용자로, Multi User Mode에서 사용자를 말합니다. 일반적으로 Android에서는 User를 여러개 생성할 수 있는데요. 그 User를 말합니다. 특정 User에 설치된 앱들에 대한 StorageStats를 얻을 수 있습니다.

아래 코드처럼 입력합니다. queryStatsForUser(UUID, UserHandle)을 입력하면 특정 User에 대한 StorageStats를 얻을 수 있습니다.

private void getStorageStatsForUser() {
    StorageStatsManager statsManager = getSystemService(StorageStatsManager.class);
    UserHandle user = android.os.Process.myUserHandle();
    try {
        StorageStats stats = statsManager.queryStatsForUser(
                UUID_DEFAULT, user);
        Log.d(TAG, "[Current User] App size: " + stats.getAppBytes() +
                ", Cache size: " + stats.getCacheBytes() +
                ", Data size: " + stats.getDataBytes());
    } catch (IOException e) {
        e.printStackTrace();
    }
}

결과는 아래처럼 출력이 됩니다.

10-08 23:33:47.944  4861  4861 D MainActivity: [Current User] App size: 36626432, Cache size: 19062784, Data size: 106500096

참고