/** * DiskUsage - displays sdcard usage on android. * Copyright (C) 2008-2011 Ivan Volosyuk * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package com.google.android.diskusage; import com.google.android.diskusage.datasource.AppInfo; import com.google.android.diskusage.datasource.AppStats; import com.google.android.diskusage.datasource.AppStatsCallback; import com.google.android.diskusage.datasource.DataSource; import com.google.android.diskusage.datasource.PkgInfo; import com.google.android.diskusage.datasource.StatFsSource; import com.google.android.diskusage.entity.FileSystemEntry; import com.google.android.diskusage.entity.FileSystemPackage; import android.content.pm.IPackageStatsObserver; import android.content.pm.PackageManager; import android.os.Handler; import android.util.Log; import java.io.BufferedReader; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.TreeMap; public class Apps2SDLoader { private final DiskUsage diskUsage; // Guarded by private int numLookups; // Guarded by currentAppName private int numLoadedPackages; // Guarded by currentAppName List<CharSequence> currentAppName = new ArrayList<CharSequence>(); // Guarded by currentAppName CharSequence lastAppName = ""; boolean switchToSecondary = true; public Apps2SDLoader(DiskUsage activity) { this.diskUsage = activity; } private synchronized void addLockup(String name) throws InterruptedException { while (numLookups >= 2) { wait(); } numLookups++; currentAppName.add(name); } private synchronized void delLookup(String name) { numLookups--; numLoadedPackages++; currentAppName.remove(name); lastAppName = name; notifyAll(); } private Map<String, Long> getDfSizes() { TreeMap<String, Long> map = new TreeMap<String, Long>(); try { // FIXME: debug BufferedReader reader = DataSource.get().getProcReader(); String line; while ((line = reader.readLine()) != null) { String[] parts = line.split(" +"); if (parts.length < 3) continue; String mountPoint = parts[1]; String fsType = parts[2]; if (fsType.equals("tmpfs")) { continue; } if (!mountPoint.startsWith("/mnt/asec/")) { continue; } String packageNameNum = mountPoint.substring(mountPoint.lastIndexOf('/') + 1); String packageName = packageNameNum.substring(0, packageNameNum.indexOf('-')); StatFsSource stat = DataSource.get().statFs(mountPoint); long size = (stat.getBlockCount() - stat.getAvailableBlocks()) * stat.getBlockSize(); map.put(packageName, size); Log.d("diskusage", "external size (" + packageName + ") = " + size / 1024 + " kb"); } reader.close(); } catch (Throwable t) { Log.e("disksusage", "failed to parse /proc/mounts", t); } return map; } public FileSystemEntry[] load( boolean sdOnly, final AppFilter appFilter, final int blockSize) throws Throwable { final Map<String, Long> dfSizes = getDfSizes(); final ArrayList<FileSystemEntry> entries = new ArrayList<FileSystemEntry>(); PackageManager pm = diskUsage.getPackageManager(); Method getPackageSizeInfo = pm.getClass().getMethod( "getPackageSizeInfo", String.class, IPackageStatsObserver.class); final List<PkgInfo> installedPackages = DataSource.get().getInstalledPackages(pm); final Handler handler = diskUsage.handler; Runnable progressUpdater = new Runnable() { @Override public void run() { MyProgressDialog dialog = diskUsage.getPersistantState().loading; if (dialog != null) { if (switchToSecondary) { dialog.switchToSecondary(); switchToSecondary = false; } dialog.setMax(installedPackages.size()); final CharSequence appName; synchronized (Apps2SDLoader.this) { if (currentAppName.size() > 0) { appName = currentAppName.get(0); } else { appName = lastAppName; } } dialog.setProgress(numLoadedPackages, appName); } diskUsage.handler.postDelayed(this, 50); } }; handler.post(progressUpdater); for (final PkgInfo info : installedPackages) { // Log.d("diskusage", "Got package: " + info.packageName); if (info.getApplicationInfo() == null) { Log.d("diskusage", "No applicationInfo"); continue; } int flag = 0x40000; // ApplicationInfo.FLAG_EXTERNAL_STORAGE final AppInfo appInfo = info.getApplicationInfo(); boolean on_sdcard = (appInfo.getFlags() & flag) != 0; if (on_sdcard || !sdOnly) { final String pkg = info.getPackageName(); final String name = appInfo.getApplicationLabel(); addLockup(name); DataSource.get().getPackageSizeInfo( info, getPackageSizeInfo, pm, new AppStatsCallback() { @Override public void onGetStatsCompleted(AppStats stats, boolean succeeded) { synchronized (Apps2SDLoader.this) { if (succeeded) { FileSystemPackage p = new FileSystemPackage( name, pkg, stats, appInfo.getFlags(), dfSizes.get(pkg), blockSize); p.applyFilter(appFilter, blockSize); entries.add(p); // Log.i("diskusage", "codeSize: " + pStats.codeSize); } delLookup(name); } } }); } else { synchronized (currentAppName) { lastAppName = appInfo.getApplicationLabel(); numLoadedPackages++; } } } while (true) { synchronized (this) { if (numLookups != 0) { wait(); } else { break; } } } if (entries.size() == 0) return null; FileSystemEntry[] result = entries.toArray(new FileSystemEntry[] {}); Arrays.sort(result, FileSystemEntry.COMPARE); handler.removeCallbacks(progressUpdater); return result; } }