Monday, 20 June 2016

Android get cache size, data size, apk size for all installed application

I had to make one application which should show the list of installed applications and their size occupied in internal memory, i struggled a lot to find the complete solution at one place. Here I ‘m sharing the full solution which i achieved. 

We can’t get the cache size directly by using PackageManager. We need to use Java Reflection and AIDL  to achieve this. 

Steps to get get cache size, data size and apk size
1. Create AIDL package and add files.
2. Create Class AppDetails.java to get installed package list.
3. Create class by extending IPackageStatsObserver.Stub.
4. Use getPackageSizeInfo method to get data.
Android get app data size using AIDL

1. Create AIDL package and add files.
Create package android.content.pm inside src/main and add the below two files.
IPackageStatsObserver.aidl
package android.content.pm;

import android.content.pm.PackageStats;
/**
 * API for package data change related callbacks from the Package Manager.
 * Some usage scenarios include deletion of cache directory, generate
 * statistics related to code, data, cache usage(TODO)
 * {@hide}
 */
oneway interface IPackageStatsObserver {

    void onGetStatsCompleted(in PackageStats pStats, boolean succeeded);
}

PackageStats.aidl
package android.content.pm;
parcelable PackageStats;


Here is the snap for my project structure:




2. Create Class AppDetails.java to get installed package list.

AppDetails.java
import android.app.Activity;
import android.content.pm.PackageInfo;
import android.graphics.drawable.Drawable;
import android.widget.ListView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by mukeshk on 6/17/2016.
 */

    public class AppDetails {
        Activity mActivity;
        public ArrayList<PackageInfoStruct> res new ArrayList<PackageInfoStruct>();
        public ListView list;
        public String app_labels[];

        public AppDetails(Activity mActivity) {
            this.mActivity = mActivity;

        }

        public ArrayList<PackageInfoStruct> getPackages() {
            ArrayList<PackageInfoStruct> apps = getInstalledApps(false);
            final int max = apps.size();
            for (int i = 0; i < max; i++) {
                apps.get(i);
            }
            return apps;
        }

        private ArrayList<PackageInfoStruct> getInstalledApps(boolean getSysPackages) {

            List<PackageInfo> packs = mActivity.getPackageManager()
                    .getInstalledPackages(0);
            try {
                app_labels new String[packs.size()];
            } catch (Exception e) {
                Toast.makeText(mActivity.getApplicationContext(), e.getMessage(),
                        Toast.LENGTH_SHORT).show();
            }
            for (int i = 0; i < packs.size(); i++) {
                PackageInfo p = packs.get(i);
                if ((!getSysPackages) && (p.versionName == null)) {
                    continue;
                }
                PackageInfoStruct newInfo = new PackageInfoStruct();
                newInfo.appname = p.applicationInfo.loadLabel(
                        mActivity.getPackageManager()).toString();
                newInfo.pname = p.packageName;
                newInfo.datadir = p.applicationInfo.dataDir;
                newInfo.versionName = p.versionName;
                newInfo.versionCode = p.versionCode;
                newInfo.icon = p.applicationInfo.loadIcon(mActivity
                        .getPackageManager());
                res.add(newInfo);

                app_labels[i] = newInfo.appname;
            }
            return res;
        }

        class PackageInfoStruct {
            String appname "";
            String pname "";
            String versionName "";
            int versionCode 0;
            Drawable icon;
            String datadir "";
        }

3. Create class by extending IPackageStatsObserver.Stub.
 I have created separate class StorageInformation which will have getpackageSize method to execute the code to calculate cache and another data. One inner class cachePackState have to create by extending IPackageStatsObserver.Stub. Here we have to onGetStatsCompleted method. Here is the complete code: 

StorageInformation.java

import android.app.Activity;
import android.content.Context;
import android.content.pm.IPackageStatsObserver;
import android.content.pm.PackageManager;
import android.content.pm.PackageStats;
import android.os.RemoteException;
import android.util.Log;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;

/**
 * This class will perform data operation
 */
public class StorageInformation {

    long packageSize 0;
    AppDetails cAppDetails;
    public ArrayList<AppDetails.PackageInfoStruct> res;
    Context context;

    public StorageInformation(Context context){
        this.context=context;
    }

    public void getpackageSize() {
        cAppDetails new AppDetails((Activity) context);
        res cAppDetails.getPackages();
        if (res == null)
            return;
        for (int m = 0; m < res.size(); m++) {
            // Create object to access Package Manager
            PackageManager pm = context.getPackageManager();
            Method getPackageSizeInfo;
            try {
                getPackageSizeInfo = pm.getClass().getMethod(
                        "getPackageSizeInfo", String.class,
                        IPackageStatsObserver.class);
                getPackageSizeInfo.invoke(pm, res.get(m).pname,
                        new cachePackState()); //Call the inner class
            catch (SecurityException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }
/*
* Inner class which will get the data size for application
* */
    private class cachePackState extends IPackageStatsObserver.Stub {
        @Override
        public void onGetStatsCompleted(PackageStats pStats, boolean succeeded)
                throws RemoteException {
            Log.w("Package Name", pStats.packageName "");
            Log.i("Cache Size", pStats.cacheSize "");
            Log.v("Data Size", pStats.dataSize "");
            packageSize = pStats.dataSize + pStats.cacheSize;
            Log.v("Total Cache Size"" " packageSize);
            Log.v("APK Size",pStats.codeSize+"");
        }
    }
}

Here I’m only logging the output data. You can display in list view with custom adapter.

Note: It may not work if in future the method name change or any thing changed in 
IPackageStatsObserver.Stub.

4. Use getPackageSizeInfo method to get data.

I have just called the getPackageSize method from MainActivity class. You can call call this method according to your project. 
MainActivity.java

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //Get the information of all the installed application data                 StorageInformation storageInformation = 
                                new  StorageInformation(MainActivity.this);
          storageInformation.getpackageSize();
   }
}

Run the application. The output can be seen in log. Here is the snap of AndroidMonitor with all data size logged. All the data unit is in byte. Please convert in to KB or MB according to your requirement. 


Please feel free to comment for any clarification or suggestion.















***