WidgetPreviewLoader.java revision 091440a9cb9d4f42406631004aa484cbb79214ca
1325dc23624160689e59fbac708cf6f222b20d025Daniel Sandlerpackage com.android.launcher3;
205713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka
305713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurkaimport android.appwidget.AppWidgetProviderInfo;
405713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurkaimport android.content.ComponentName;
505713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurkaimport android.content.ContentValues;
605713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurkaimport android.content.Context;
78ff02cac07a9c7fb0f321e93d67ad75cc588fbaeMichael Jurkaimport android.content.SharedPreferences;
805713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurkaimport android.content.pm.ResolveInfo;
905713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurkaimport android.content.res.Resources;
1005713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurkaimport android.database.Cursor;
111f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roosimport android.database.sqlite.SQLiteCantOpenDatabaseException;
1205713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurkaimport android.database.sqlite.SQLiteDatabase;
136e27f642ae66dd1920b25b527fced7268943d11aMichael Jurkaimport android.database.sqlite.SQLiteDiskIOException;
1405713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurkaimport android.database.sqlite.SQLiteOpenHelper;
155f05913f71da68a6c406f905ec6979586e3ba27aWinson Chungimport android.database.sqlite.SQLiteReadOnlyDatabaseException;
1605713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurkaimport android.graphics.Bitmap;
1705713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurkaimport android.graphics.Bitmap.Config;
1805713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurkaimport android.graphics.BitmapFactory;
1905713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurkaimport android.graphics.Canvas;
2005713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurkaimport android.graphics.ColorMatrix;
2105713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurkaimport android.graphics.ColorMatrixColorFilter;
2205713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurkaimport android.graphics.Paint;
2305713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurkaimport android.graphics.PorterDuff;
2405713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurkaimport android.graphics.Rect;
254cad7538c9615303d291f5b52e960aaf0985828fSunny Goyalimport android.graphics.RectF;
2605713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurkaimport android.graphics.drawable.BitmapDrawable;
2705713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurkaimport android.graphics.drawable.Drawable;
2805713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurkaimport android.os.AsyncTask;
295f05913f71da68a6c406f905ec6979586e3ba27aWinson Chungimport android.os.Build;
3005713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurkaimport android.util.Log;
314cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal
32ffe83f13319cdb833a25596d47cf1af098cc4c60Sunny Goyalimport com.android.launcher3.compat.AppWidgetManagerCompat;
33091440a9cb9d4f42406631004aa484cbb79214caAdam Cohenimport com.android.launcher3.util.Thunk;
34ffe83f13319cdb833a25596d47cf1af098cc4c60Sunny Goyal
3505713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurkaimport java.io.ByteArrayOutputStream;
3605713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurkaimport java.io.File;
371f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roosimport java.io.IOException;
3805713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurkaimport java.lang.ref.SoftReference;
3905713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurkaimport java.lang.ref.WeakReference;
4005713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurkaimport java.util.ArrayList;
411f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roosimport java.util.Arrays;
4205713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurkaimport java.util.HashMap;
4305713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurkaimport java.util.HashSet;
441f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roosimport java.util.List;
4565d60e21ec22e3bf03ba39f7a0be24df2cc914a2Adrian Roosimport java.util.concurrent.Callable;
4665d60e21ec22e3bf03ba39f7a0be24df2cc914a2Adrian Roosimport java.util.concurrent.ExecutionException;
4705713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka
48ffe83f13319cdb833a25596d47cf1af098cc4c60Sunny Goyalpublic class WidgetPreviewLoader {
4905713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka
50ffe83f13319cdb833a25596d47cf1af098cc4c60Sunny Goyal    private static final String TAG = "WidgetPreviewLoader";
51ffe83f13319cdb833a25596d47cf1af098cc4c60Sunny Goyal    private static final String ANDROID_INCREMENTAL_VERSION_NAME_KEY = "android.incremental.version";
52ffe83f13319cdb833a25596d47cf1af098cc4c60Sunny Goyal
53ffe83f13319cdb833a25596d47cf1af098cc4c60Sunny Goyal    private static final float WIDGET_PREVIEW_ICON_PADDING_PERCENTAGE = 0.25f;
54091440a9cb9d4f42406631004aa484cbb79214caAdam Cohen    @Thunk static final HashSet<String> sInvalidPackages = new HashSet<String>();
55ffe83f13319cdb833a25596d47cf1af098cc4c60Sunny Goyal
56b745afbdd75157c73d581b345118cdaff99e912dWinson Chung    private final HashMap<String, WeakReference<Bitmap>> mLoadedPreviews = new HashMap<>();
57b745afbdd75157c73d581b345118cdaff99e912dWinson Chung    private final ArrayList<SoftReference<Bitmap>> mUnusedBitmaps = new ArrayList<>();
58ffe83f13319cdb833a25596d47cf1af098cc4c60Sunny Goyal
59ffe83f13319cdb833a25596d47cf1af098cc4c60Sunny Goyal    private final int mAppIconSize;
604cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal    private final int mCellWidth;
614cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal
624cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal    private final Context mContext;
63ffe83f13319cdb833a25596d47cf1af098cc4c60Sunny Goyal    private final IconCache mIconCache;
64ffe83f13319cdb833a25596d47cf1af098cc4c60Sunny Goyal    private final AppWidgetManagerCompat mManager;
6505713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka
663f4e070aa58d51dd136885b4d3e2e6c5d9f93ea0Michael Jurka    private int mPreviewBitmapWidth;
673f4e070aa58d51dd136885b4d3e2e6c5d9f93ea0Michael Jurka    private int mPreviewBitmapHeight;
6805713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka    private String mSize;
6905713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka
7005713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka    private String mCachedSelectQuery;
71d9cb4a124ac5c83080ace5ac92980df9c6f49e8eMichael Jurka    private CacheDb mDb;
7205713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka
7365d60e21ec22e3bf03ba39f7a0be24df2cc914a2Adrian Roos    private final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor();
7465d60e21ec22e3bf03ba39f7a0be24df2cc914a2Adrian Roos
75fd13c714f412dfa8130fa7a664cb1692a565c539Chris Wren    public WidgetPreviewLoader(Context context) {
765f8afe6280eae34620067696173e71943e1a30a3Winson Chung        LauncherAppState app = LauncherAppState.getInstance();
775f8afe6280eae34620067696173e71943e1a30a3Winson Chung        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
785f8afe6280eae34620067696173e71943e1a30a3Winson Chung
79fd13c714f412dfa8130fa7a664cb1692a565c539Chris Wren        mContext = context;
805f8afe6280eae34620067696173e71943e1a30a3Winson Chung        mAppIconSize = grid.iconSizePx;
814cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal        mCellWidth = grid.cellWidthPx;
824cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal
83d9cb4a124ac5c83080ace5ac92980df9c6f49e8eMichael Jurka        mIconCache = app.getIconCache();
84ffe83f13319cdb833a25596d47cf1af098cc4c60Sunny Goyal        mManager = AppWidgetManagerCompat.getInstance(context);
85d9cb4a124ac5c83080ace5ac92980df9c6f49e8eMichael Jurka        mDb = app.getWidgetPreviewCacheDb();
868ff02cac07a9c7fb0f321e93d67ad75cc588fbaeMichael Jurka
878ff02cac07a9c7fb0f321e93d67ad75cc588fbaeMichael Jurka        SharedPreferences sp = context.getSharedPreferences(
888ff02cac07a9c7fb0f321e93d67ad75cc588fbaeMichael Jurka                LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE);
898ff02cac07a9c7fb0f321e93d67ad75cc588fbaeMichael Jurka        final String lastVersionName = sp.getString(ANDROID_INCREMENTAL_VERSION_NAME_KEY, null);
908ff02cac07a9c7fb0f321e93d67ad75cc588fbaeMichael Jurka        final String versionName = android.os.Build.VERSION.INCREMENTAL;
910250945759f7ac58aab912448d62dc108b1770c5Adam Cohen        final boolean isLollipopOrGreater = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
928ff02cac07a9c7fb0f321e93d67ad75cc588fbaeMichael Jurka        if (!versionName.equals(lastVersionName)) {
935f05913f71da68a6c406f905ec6979586e3ba27aWinson Chung            try {
945f05913f71da68a6c406f905ec6979586e3ba27aWinson Chung                // clear all the previews whenever the system version changes, to ensure that
955f05913f71da68a6c406f905ec6979586e3ba27aWinson Chung                // previews are up-to-date for any apps that might have been updated with the system
965f05913f71da68a6c406f905ec6979586e3ba27aWinson Chung                clearDb();
975f05913f71da68a6c406f905ec6979586e3ba27aWinson Chung            } catch (SQLiteReadOnlyDatabaseException e) {
980250945759f7ac58aab912448d62dc108b1770c5Adam Cohen                if (isLollipopOrGreater) {
995f05913f71da68a6c406f905ec6979586e3ba27aWinson Chung                    // Workaround for Bug. 18554839, if we fail to clear the db due to the read-only
1005f05913f71da68a6c406f905ec6979586e3ba27aWinson Chung                    // issue, then ignore this error and leave the old previews
1015f05913f71da68a6c406f905ec6979586e3ba27aWinson Chung                } else {
1025f05913f71da68a6c406f905ec6979586e3ba27aWinson Chung                    throw e;
1035f05913f71da68a6c406f905ec6979586e3ba27aWinson Chung                }
1045f05913f71da68a6c406f905ec6979586e3ba27aWinson Chung            } finally {
1055f05913f71da68a6c406f905ec6979586e3ba27aWinson Chung                SharedPreferences.Editor editor = sp.edit();
1065f05913f71da68a6c406f905ec6979586e3ba27aWinson Chung                editor.putString(ANDROID_INCREMENTAL_VERSION_NAME_KEY, versionName);
1075f05913f71da68a6c406f905ec6979586e3ba27aWinson Chung                editor.commit();
1085f05913f71da68a6c406f905ec6979586e3ba27aWinson Chung            }
1098ff02cac07a9c7fb0f321e93d67ad75cc588fbaeMichael Jurka        }
1103f4e070aa58d51dd136885b4d3e2e6c5d9f93ea0Michael Jurka    }
111ffe83f13319cdb833a25596d47cf1af098cc4c60Sunny Goyal
1126e27f642ae66dd1920b25b527fced7268943d11aMichael Jurka    public void recreateDb() {
1136e27f642ae66dd1920b25b527fced7268943d11aMichael Jurka        LauncherAppState app = LauncherAppState.getInstance();
1146e27f642ae66dd1920b25b527fced7268943d11aMichael Jurka        app.recreateWidgetPreviewDb();
1156e27f642ae66dd1920b25b527fced7268943d11aMichael Jurka        mDb = app.getWidgetPreviewCacheDb();
1166e27f642ae66dd1920b25b527fced7268943d11aMichael Jurka    }
1173f4e070aa58d51dd136885b4d3e2e6c5d9f93ea0Michael Jurka
1184cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal    public void setPreviewSize(int previewWidth, int previewHeight) {
1193f4e070aa58d51dd136885b4d3e2e6c5d9f93ea0Michael Jurka        mPreviewBitmapWidth = previewWidth;
1203f4e070aa58d51dd136885b4d3e2e6c5d9f93ea0Michael Jurka        mPreviewBitmapHeight = previewHeight;
1213f4e070aa58d51dd136885b4d3e2e6c5d9f93ea0Michael Jurka        mSize = previewWidth + "x" + previewHeight;
12205713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka    }
12305713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka
12405713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka    public Bitmap getPreview(final Object o) {
125eb1bb920507fe7f27b2ecece5b67749dac7850f3Michael Jurka        final String name = getObjectName(o);
126eb1bb920507fe7f27b2ecece5b67749dac7850f3Michael Jurka        final String packageName = getObjectPackage(o);
12705713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        // check if the package is valid
12805713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        synchronized(sInvalidPackages) {
1295d2704fbb0a7bb4763d4e2ee031e16a8913ba003Adrian Roos            boolean packageValid = !sInvalidPackages.contains(packageName);
1305d2704fbb0a7bb4763d4e2ee031e16a8913ba003Adrian Roos            if (!packageValid) {
1315d2704fbb0a7bb4763d4e2ee031e16a8913ba003Adrian Roos                return null;
1325d2704fbb0a7bb4763d4e2ee031e16a8913ba003Adrian Roos            }
13305713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        }
1345d2704fbb0a7bb4763d4e2ee031e16a8913ba003Adrian Roos        synchronized(mLoadedPreviews) {
1355d2704fbb0a7bb4763d4e2ee031e16a8913ba003Adrian Roos            // check if it exists in our existing cache
1365d2704fbb0a7bb4763d4e2ee031e16a8913ba003Adrian Roos            if (mLoadedPreviews.containsKey(name)) {
1375d2704fbb0a7bb4763d4e2ee031e16a8913ba003Adrian Roos                WeakReference<Bitmap> bitmapReference = mLoadedPreviews.get(name);
1385d2704fbb0a7bb4763d4e2ee031e16a8913ba003Adrian Roos                Bitmap bitmap = bitmapReference.get();
1395d2704fbb0a7bb4763d4e2ee031e16a8913ba003Adrian Roos                if (bitmap != null) {
1405d2704fbb0a7bb4763d4e2ee031e16a8913ba003Adrian Roos                    return bitmap;
14105713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka                }
14205713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            }
14305713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        }
14405713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka
14505713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        Bitmap unusedBitmap = null;
1463f4e070aa58d51dd136885b4d3e2e6c5d9f93ea0Michael Jurka        synchronized(mUnusedBitmaps) {
14705713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            // not in cache; we need to load it from the db
1485d2704fbb0a7bb4763d4e2ee031e16a8913ba003Adrian Roos            while (unusedBitmap == null && mUnusedBitmaps.size() > 0) {
1495d2704fbb0a7bb4763d4e2ee031e16a8913ba003Adrian Roos                Bitmap candidate = mUnusedBitmaps.remove(0).get();
1505d2704fbb0a7bb4763d4e2ee031e16a8913ba003Adrian Roos                if (candidate != null && candidate.isMutable() &&
1515d2704fbb0a7bb4763d4e2ee031e16a8913ba003Adrian Roos                        candidate.getWidth() == mPreviewBitmapWidth &&
1525d2704fbb0a7bb4763d4e2ee031e16a8913ba003Adrian Roos                        candidate.getHeight() == mPreviewBitmapHeight) {
1535d2704fbb0a7bb4763d4e2ee031e16a8913ba003Adrian Roos                    unusedBitmap = candidate;
1545d2704fbb0a7bb4763d4e2ee031e16a8913ba003Adrian Roos                }
15505713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            }
15605713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        }
15705713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka
15805713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        if (unusedBitmap == null) {
1593f4e070aa58d51dd136885b4d3e2e6c5d9f93ea0Michael Jurka            unusedBitmap = Bitmap.createBitmap(mPreviewBitmapWidth, mPreviewBitmapHeight,
16005713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka                    Bitmap.Config.ARGB_8888);
16105713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        }
1625d2704fbb0a7bb4763d4e2ee031e16a8913ba003Adrian Roos        Bitmap preview = readFromDb(name, unusedBitmap);
16305713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka
16405713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        if (preview != null) {
1653f4e070aa58d51dd136885b4d3e2e6c5d9f93ea0Michael Jurka            synchronized(mLoadedPreviews) {
1663f4e070aa58d51dd136885b4d3e2e6c5d9f93ea0Michael Jurka                mLoadedPreviews.put(name, new WeakReference<Bitmap>(preview));
16705713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            }
16805713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            return preview;
16905713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        } else {
17005713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            // it's not in the db... we need to generate it
17105713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            final Bitmap generatedPreview = generatePreview(o, unusedBitmap);
17205713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            preview = generatedPreview;
17305713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            if (preview != unusedBitmap) {
17405713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka                throw new RuntimeException("generatePreview is not recycling the bitmap " + o);
17505713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            }
17605713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka
1773f4e070aa58d51dd136885b4d3e2e6c5d9f93ea0Michael Jurka            synchronized(mLoadedPreviews) {
1783f4e070aa58d51dd136885b4d3e2e6c5d9f93ea0Michael Jurka                mLoadedPreviews.put(name, new WeakReference<Bitmap>(preview));
17905713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            }
18005713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka
18105713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            // write to db on a thread pool... this can be done lazily and improves the performance
18205713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            // of the first time widget previews are loaded
18305713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            new AsyncTask<Void, Void, Void>() {
18405713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka                public Void doInBackground(Void ... args) {
18505713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka                    writeToDb(o, generatedPreview);
18605713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka                    return null;
18705713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka                }
18805713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
18905713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka
19005713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            return preview;
19105713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        }
19205713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka    }
19305713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka
194ee8e99fe3bde78885904b4d9ea789b4d2a6f2b16Michael Jurka    public void recycleBitmap(Object o, Bitmap bitmapToRecycle) {
19505713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        String name = getObjectName(o);
1965140cfaf471ed21c1cdcf0edcb53ae89b87c6848Michael Jurka        synchronized (mLoadedPreviews) {
1975140cfaf471ed21c1cdcf0edcb53ae89b87c6848Michael Jurka            if (mLoadedPreviews.containsKey(name)) {
1983f4e070aa58d51dd136885b4d3e2e6c5d9f93ea0Michael Jurka                Bitmap b = mLoadedPreviews.get(name).get();
199ee8e99fe3bde78885904b4d9ea789b4d2a6f2b16Michael Jurka                if (b == bitmapToRecycle) {
2003f4e070aa58d51dd136885b4d3e2e6c5d9f93ea0Michael Jurka                    mLoadedPreviews.remove(name);
201ee8e99fe3bde78885904b4d9ea789b4d2a6f2b16Michael Jurka                    if (bitmapToRecycle.isMutable()) {
2025140cfaf471ed21c1cdcf0edcb53ae89b87c6848Michael Jurka                        synchronized (mUnusedBitmaps) {
2035140cfaf471ed21c1cdcf0edcb53ae89b87c6848Michael Jurka                            mUnusedBitmaps.add(new SoftReference<Bitmap>(b));
2045140cfaf471ed21c1cdcf0edcb53ae89b87c6848Michael Jurka                        }
20505713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka                    }
20605713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka                } else {
20705713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka                    throw new RuntimeException("Bitmap passed in doesn't match up");
20805713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka                }
20905713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            }
21005713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        }
21105713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka    }
21205713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka
213d9cb4a124ac5c83080ace5ac92980df9c6f49e8eMichael Jurka    static class CacheDb extends SQLiteOpenHelper {
214e5919c5574ff09b88173b44558c6d325841511d6Michael Jurka        final static int DB_VERSION = 2;
21505713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        final static String TABLE_NAME = "shortcut_and_widget_previews";
21605713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        final static String COLUMN_NAME = "name";
21705713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        final static String COLUMN_SIZE = "size";
21805713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        final static String COLUMN_PREVIEW_BITMAP = "preview_bitmap";
21905713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        Context mContext;
22005713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka
221d9cb4a124ac5c83080ace5ac92980df9c6f49e8eMichael Jurka        public CacheDb(Context context) {
2224fbbb3e3e0e135bc5b304bc63637bbc8b54fb5d8Helena Josol            super(context, new File(context.getCacheDir(),
2234fbbb3e3e0e135bc5b304bc63637bbc8b54fb5d8Helena Josol                    LauncherFiles.WIDGET_PREVIEWS_DB).getPath(), null, DB_VERSION);
22405713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            // Store the context for later use
22505713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            mContext = context;
22605713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        }
22705713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka
22805713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        @Override
22905713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        public void onCreate(SQLiteDatabase database) {
23032b7a097338b77d8999c706c42f19e8ba060ba62Michael Jurka            database.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " (" +
23105713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka                    COLUMN_NAME + " TEXT NOT NULL, " +
23205713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka                    COLUMN_SIZE + " TEXT NOT NULL, " +
23305713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka                    COLUMN_PREVIEW_BITMAP + " BLOB NOT NULL, " +
23405713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka                    "PRIMARY KEY (" + COLUMN_NAME + ", " + COLUMN_SIZE + ") " +
23532b7a097338b77d8999c706c42f19e8ba060ba62Michael Jurka                    ");");
23605713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        }
23705713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka
23805713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        @Override
23905713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
240e5919c5574ff09b88173b44558c6d325841511d6Michael Jurka            if (oldVersion != newVersion) {
241e5919c5574ff09b88173b44558c6d325841511d6Michael Jurka                // Delete all the records; they'll be repopulated as this is a cache
242e5919c5574ff09b88173b44558c6d325841511d6Michael Jurka                db.execSQL("DELETE FROM " + TABLE_NAME);
243e5919c5574ff09b88173b44558c6d325841511d6Michael Jurka            }
24405713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        }
24505713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka    }
24605713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka
24705713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka    private static final String WIDGET_PREFIX = "Widget:";
24805713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka    private static final String SHORTCUT_PREFIX = "Shortcut:";
24905713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka
25005713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka    private static String getObjectName(Object o) {
25105713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        // should cache the string builder
25205713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        StringBuilder sb = new StringBuilder();
25305713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        String output;
25405713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        if (o instanceof AppWidgetProviderInfo) {
25505713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            sb.append(WIDGET_PREFIX);
256ffe83f13319cdb833a25596d47cf1af098cc4c60Sunny Goyal            sb.append(((AppWidgetProviderInfo) o).toString());
25705713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            output = sb.toString();
25805713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            sb.setLength(0);
25905713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        } else {
26005713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            sb.append(SHORTCUT_PREFIX);
26105713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            ResolveInfo info = (ResolveInfo) o;
26205713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            sb.append(new ComponentName(info.activityInfo.packageName,
26305713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka                    info.activityInfo.name).flattenToString());
26405713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            output = sb.toString();
26505713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            sb.setLength(0);
26605713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        }
26705713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        return output;
26805713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka    }
26905713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka
27005713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka    private String getObjectPackage(Object o) {
27105713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        if (o instanceof AppWidgetProviderInfo) {
27205713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            return ((AppWidgetProviderInfo) o).provider.getPackageName();
27305713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        } else {
27405713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            ResolveInfo info = (ResolveInfo) o;
27505713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            return info.activityInfo.packageName;
27605713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        }
27705713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka    }
27805713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka
279091440a9cb9d4f42406631004aa484cbb79214caAdam Cohen    @Thunk void writeToDb(Object o, Bitmap preview) {
28005713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        String name = getObjectName(o);
28105713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        SQLiteDatabase db = mDb.getWritableDatabase();
28205713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        ContentValues values = new ContentValues();
28305713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka
284d9cb4a124ac5c83080ace5ac92980df9c6f49e8eMichael Jurka        values.put(CacheDb.COLUMN_NAME, name);
28505713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        ByteArrayOutputStream stream = new ByteArrayOutputStream();
28605713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        preview.compress(Bitmap.CompressFormat.PNG, 100, stream);
287d9cb4a124ac5c83080ace5ac92980df9c6f49e8eMichael Jurka        values.put(CacheDb.COLUMN_PREVIEW_BITMAP, stream.toByteArray());
288d9cb4a124ac5c83080ace5ac92980df9c6f49e8eMichael Jurka        values.put(CacheDb.COLUMN_SIZE, mSize);
2896e27f642ae66dd1920b25b527fced7268943d11aMichael Jurka        try {
2906e27f642ae66dd1920b25b527fced7268943d11aMichael Jurka            db.insert(CacheDb.TABLE_NAME, null, values);
2916e27f642ae66dd1920b25b527fced7268943d11aMichael Jurka        } catch (SQLiteDiskIOException e) {
2926e27f642ae66dd1920b25b527fced7268943d11aMichael Jurka            recreateDb();
2931f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos        } catch (SQLiteCantOpenDatabaseException e) {
2941f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos            dumpOpenFiles();
2951f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos            throw e;
2966e27f642ae66dd1920b25b527fced7268943d11aMichael Jurka        }
29705713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka    }
29805713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka
2998ff02cac07a9c7fb0f321e93d67ad75cc588fbaeMichael Jurka    private void clearDb() {
3008ff02cac07a9c7fb0f321e93d67ad75cc588fbaeMichael Jurka        SQLiteDatabase db = mDb.getWritableDatabase();
3018ff02cac07a9c7fb0f321e93d67ad75cc588fbaeMichael Jurka        // Delete everything
3026e27f642ae66dd1920b25b527fced7268943d11aMichael Jurka        try {
3036e27f642ae66dd1920b25b527fced7268943d11aMichael Jurka            db.delete(CacheDb.TABLE_NAME, null, null);
3046e27f642ae66dd1920b25b527fced7268943d11aMichael Jurka        } catch (SQLiteDiskIOException e) {
3051f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos        } catch (SQLiteCantOpenDatabaseException e) {
3061f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos            dumpOpenFiles();
3071f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos            throw e;
3086e27f642ae66dd1920b25b527fced7268943d11aMichael Jurka        }
3098ff02cac07a9c7fb0f321e93d67ad75cc588fbaeMichael Jurka    }
3108ff02cac07a9c7fb0f321e93d67ad75cc588fbaeMichael Jurka
311eb1bb920507fe7f27b2ecece5b67749dac7850f3Michael Jurka    public static void removePackageFromDb(final CacheDb cacheDb, final String packageName) {
31205713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        synchronized(sInvalidPackages) {
31305713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            sInvalidPackages.add(packageName);
31405713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        }
31505713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        new AsyncTask<Void, Void, Void>() {
31605713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            public Void doInBackground(Void ... args) {
317d9cb4a124ac5c83080ace5ac92980df9c6f49e8eMichael Jurka                SQLiteDatabase db = cacheDb.getWritableDatabase();
3186e27f642ae66dd1920b25b527fced7268943d11aMichael Jurka                try {
3196e27f642ae66dd1920b25b527fced7268943d11aMichael Jurka                    db.delete(CacheDb.TABLE_NAME,
3206e27f642ae66dd1920b25b527fced7268943d11aMichael Jurka                            CacheDb.COLUMN_NAME + " LIKE ? OR " +
3216e27f642ae66dd1920b25b527fced7268943d11aMichael Jurka                            CacheDb.COLUMN_NAME + " LIKE ?", // SELECT query
3226e27f642ae66dd1920b25b527fced7268943d11aMichael Jurka                            new String[] {
3236e27f642ae66dd1920b25b527fced7268943d11aMichael Jurka                                    WIDGET_PREFIX + packageName + "/%",
3246e27f642ae66dd1920b25b527fced7268943d11aMichael Jurka                                    SHORTCUT_PREFIX + packageName + "/%"
3256e27f642ae66dd1920b25b527fced7268943d11aMichael Jurka                            } // args to SELECT query
3266e27f642ae66dd1920b25b527fced7268943d11aMichael Jurka                    );
3276e27f642ae66dd1920b25b527fced7268943d11aMichael Jurka                } catch (SQLiteDiskIOException e) {
3281f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                } catch (SQLiteCantOpenDatabaseException e) {
3291f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                    dumpOpenFiles();
3301f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                    throw e;
3316e27f642ae66dd1920b25b527fced7268943d11aMichael Jurka                }
33205713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka                synchronized(sInvalidPackages) {
33305713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka                    sInvalidPackages.remove(packageName);
33405713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka                }
33505713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka                return null;
33605713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            }
33705713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
33805713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka    }
33905713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka
340ffe83f13319cdb833a25596d47cf1af098cc4c60Sunny Goyal    private static void removeItemFromDb(final CacheDb cacheDb, final String objectName) {
341eb1bb920507fe7f27b2ecece5b67749dac7850f3Michael Jurka        new AsyncTask<Void, Void, Void>() {
342eb1bb920507fe7f27b2ecece5b67749dac7850f3Michael Jurka            public Void doInBackground(Void ... args) {
343eb1bb920507fe7f27b2ecece5b67749dac7850f3Michael Jurka                SQLiteDatabase db = cacheDb.getWritableDatabase();
3446e27f642ae66dd1920b25b527fced7268943d11aMichael Jurka                try {
3456e27f642ae66dd1920b25b527fced7268943d11aMichael Jurka                    db.delete(CacheDb.TABLE_NAME,
3466e27f642ae66dd1920b25b527fced7268943d11aMichael Jurka                            CacheDb.COLUMN_NAME + " = ? ", // SELECT query
3476e27f642ae66dd1920b25b527fced7268943d11aMichael Jurka                            new String[] { objectName }); // args to SELECT query
3486e27f642ae66dd1920b25b527fced7268943d11aMichael Jurka                } catch (SQLiteDiskIOException e) {
3491f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                } catch (SQLiteCantOpenDatabaseException e) {
3501f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                    dumpOpenFiles();
3511f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                    throw e;
3526e27f642ae66dd1920b25b527fced7268943d11aMichael Jurka                }
353eb1bb920507fe7f27b2ecece5b67749dac7850f3Michael Jurka                return null;
354eb1bb920507fe7f27b2ecece5b67749dac7850f3Michael Jurka            }
355eb1bb920507fe7f27b2ecece5b67749dac7850f3Michael Jurka        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
356eb1bb920507fe7f27b2ecece5b67749dac7850f3Michael Jurka    }
357eb1bb920507fe7f27b2ecece5b67749dac7850f3Michael Jurka
35805713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka    private Bitmap readFromDb(String name, Bitmap b) {
35905713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        if (mCachedSelectQuery == null) {
360d9cb4a124ac5c83080ace5ac92980df9c6f49e8eMichael Jurka            mCachedSelectQuery = CacheDb.COLUMN_NAME + " = ? AND " +
361d9cb4a124ac5c83080ace5ac92980df9c6f49e8eMichael Jurka                    CacheDb.COLUMN_SIZE + " = ?";
36205713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        }
36305713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        SQLiteDatabase db = mDb.getReadableDatabase();
3646e27f642ae66dd1920b25b527fced7268943d11aMichael Jurka        Cursor result;
3656e27f642ae66dd1920b25b527fced7268943d11aMichael Jurka        try {
3666e27f642ae66dd1920b25b527fced7268943d11aMichael Jurka            result = db.query(CacheDb.TABLE_NAME,
3676e27f642ae66dd1920b25b527fced7268943d11aMichael Jurka                    new String[] { CacheDb.COLUMN_PREVIEW_BITMAP }, // cols to return
3686e27f642ae66dd1920b25b527fced7268943d11aMichael Jurka                    mCachedSelectQuery, // select query
3696e27f642ae66dd1920b25b527fced7268943d11aMichael Jurka                    new String[] { name, mSize }, // args to select query
3706e27f642ae66dd1920b25b527fced7268943d11aMichael Jurka                    null,
3716e27f642ae66dd1920b25b527fced7268943d11aMichael Jurka                    null,
3726e27f642ae66dd1920b25b527fced7268943d11aMichael Jurka                    null,
3736e27f642ae66dd1920b25b527fced7268943d11aMichael Jurka                    null);
3746e27f642ae66dd1920b25b527fced7268943d11aMichael Jurka        } catch (SQLiteDiskIOException e) {
3756e27f642ae66dd1920b25b527fced7268943d11aMichael Jurka            recreateDb();
3766e27f642ae66dd1920b25b527fced7268943d11aMichael Jurka            return null;
3771f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos        } catch (SQLiteCantOpenDatabaseException e) {
3781f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos            dumpOpenFiles();
3791f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos            throw e;
3806e27f642ae66dd1920b25b527fced7268943d11aMichael Jurka        }
38105713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        if (result.getCount() > 0) {
38205713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            result.moveToFirst();
38305713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            byte[] blob = result.getBlob(0);
38405713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            result.close();
3854cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal            final BitmapFactory.Options opts = new BitmapFactory.Options();
38605713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            opts.inBitmap = b;
38705713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            opts.inSampleSize = 1;
388eb1bb920507fe7f27b2ecece5b67749dac7850f3Michael Jurka            try {
389eb1bb920507fe7f27b2ecece5b67749dac7850f3Michael Jurka                return BitmapFactory.decodeByteArray(blob, 0, blob.length, opts);
390eb1bb920507fe7f27b2ecece5b67749dac7850f3Michael Jurka            } catch (IllegalArgumentException e) {
391eb1bb920507fe7f27b2ecece5b67749dac7850f3Michael Jurka                removeItemFromDb(mDb, name);
392eb1bb920507fe7f27b2ecece5b67749dac7850f3Michael Jurka                return null;
393eb1bb920507fe7f27b2ecece5b67749dac7850f3Michael Jurka            }
39405713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        } else {
39505713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            result.close();
39605713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            return null;
39705713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        }
39805713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka    }
39905713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka
400ffe83f13319cdb833a25596d47cf1af098cc4c60Sunny Goyal    private Bitmap generatePreview(Object info, Bitmap preview) {
40105713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        if (preview != null &&
4023f4e070aa58d51dd136885b4d3e2e6c5d9f93ea0Michael Jurka                (preview.getWidth() != mPreviewBitmapWidth ||
4033f4e070aa58d51dd136885b4d3e2e6c5d9f93ea0Michael Jurka                preview.getHeight() != mPreviewBitmapHeight)) {
40405713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            throw new RuntimeException("Improperly sized bitmap passed as argument");
40505713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        }
4065940042d39b576553c2499bcf3d0641281e6ad52Adam Cohen        if (info instanceof LauncherAppWidgetProviderInfo) {
4075940042d39b576553c2499bcf3d0641281e6ad52Adam Cohen            return generateWidgetPreview((LauncherAppWidgetProviderInfo) info, preview);
40805713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        } else {
40905713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            return generateShortcutPreview(
4103f4e070aa58d51dd136885b4d3e2e6c5d9f93ea0Michael Jurka                    (ResolveInfo) info, mPreviewBitmapWidth, mPreviewBitmapHeight, preview);
41105713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        }
41205713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka    }
41305713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka
4145940042d39b576553c2499bcf3d0641281e6ad52Adam Cohen    public Bitmap generateWidgetPreview(LauncherAppWidgetProviderInfo info, Bitmap preview) {
4155940042d39b576553c2499bcf3d0641281e6ad52Adam Cohen        int maxWidth = maxWidthForWidgetPreview(info.spanX);
4164cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal        return generateWidgetPreview(info, maxWidth, preview, null);
41705713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka    }
41805713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka
41905713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka    public int maxWidthForWidgetPreview(int spanX) {
4204cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal        return Math.min(mPreviewBitmapWidth, spanX * mCellWidth);
42105713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka    }
42205713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka
4234cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal    public Bitmap generateWidgetPreview(LauncherAppWidgetProviderInfo info,
4244cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal            int maxPreviewWidth, Bitmap preview, int[] preScaledWidthOut) {
42505713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        // Load the preview image if possible
42605713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        if (maxPreviewWidth < 0) maxPreviewWidth = Integer.MAX_VALUE;
42705713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka
42805713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        Drawable drawable = null;
429ffe83f13319cdb833a25596d47cf1af098cc4c60Sunny Goyal        if (info.previewImage != 0) {
430ffe83f13319cdb833a25596d47cf1af098cc4c60Sunny Goyal            drawable = mManager.loadPreview(info);
431fa9ffc28fd4d230cf38b55840238f5595716abc8Adrian Roos            if (drawable != null) {
432fa9ffc28fd4d230cf38b55840238f5595716abc8Adrian Roos                drawable = mutateOnMainThread(drawable);
433fa9ffc28fd4d230cf38b55840238f5595716abc8Adrian Roos            } else {
43405713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka                Log.w(TAG, "Can't load widget preview drawable 0x" +
435ffe83f13319cdb833a25596d47cf1af098cc4c60Sunny Goyal                        Integer.toHexString(info.previewImage) + " for provider: " + info.provider);
43605713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            }
43705713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        }
43805713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka
4394cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal        final boolean widgetPreviewExists = (drawable != null);
4404cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal        final int spanX = info.spanX < 1 ? 1 : info.spanX;
4414cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal        final int spanY = info.spanY < 1 ? 1 : info.spanY;
4424cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal
44305713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        int previewWidth;
44405713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        int previewHeight;
4454cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal        Bitmap tileBitmap = null;
4464cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal
44705713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        if (widgetPreviewExists) {
44805713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            previewWidth = drawable.getIntrinsicWidth();
44905713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            previewHeight = drawable.getIntrinsicHeight();
45005713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        } else {
45105713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            // Generate a preview image if we couldn't load one
4524cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal            tileBitmap = ((BitmapDrawable) mContext.getResources().getDrawable(
4534cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal                    R.drawable.widget_tile)).getBitmap();
4544cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal            previewWidth = tileBitmap.getWidth() * spanX;
4554cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal            previewHeight = tileBitmap.getHeight() * spanY;
45605713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        }
45705713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka
45805713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        // Scale to fit width only - let the widget preview be clipped in the
45905713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        // vertical dimension
46005713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        float scale = 1f;
46105713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        if (preScaledWidthOut != null) {
46205713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            preScaledWidthOut[0] = previewWidth;
46305713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        }
46405713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        if (previewWidth > maxPreviewWidth) {
46505713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            scale = maxPreviewWidth / (float) previewWidth;
46605713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        }
46705713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        if (scale != 1f) {
46805713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            previewWidth = (int) (scale * previewWidth);
46905713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            previewHeight = (int) (scale * previewHeight);
47005713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        }
47105713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka
47205713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        // If a bitmap is passed in, we use it; otherwise, we create a bitmap of the right size
4734cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal        final Canvas c = new Canvas();
47405713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        if (preview == null) {
47505713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            preview = Bitmap.createBitmap(previewWidth, previewHeight, Config.ARGB_8888);
4764cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal            c.setBitmap(preview);
4774cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal        } else {
4784cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal            // Reusing bitmap. Clear it.
4794cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal            c.setBitmap(preview);
4804cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal            c.drawColor(0, PorterDuff.Mode.CLEAR);
48105713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        }
48205713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka
48305713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        // Draw the scaled preview into the final bitmap
48405713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        int x = (preview.getWidth() - previewWidth) / 2;
48505713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        if (widgetPreviewExists) {
4864cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal            drawable.setBounds(x, 0, x + previewWidth, previewHeight);
4874cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal            drawable.draw(c);
48805713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        } else {
4894cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal            final Paint p = new Paint();
4904cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal            p.setFilterBitmap(true);
4914cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal
4924cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal            // draw the spanX x spanY tiles
4934cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal            final Rect src = new Rect(0, 0, tileBitmap.getWidth(), tileBitmap.getHeight());
4944cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal
4954cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal            float tileW = scale * tileBitmap.getWidth();
4964cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal            float tileH = scale * tileBitmap.getHeight();
4974cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal            final RectF dst = new RectF(0, 0, tileW, tileH);
4984cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal
4994cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal            float tx = x;
5004cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal            for (int i = 0; i < spanX; i++, tx += tileW) {
5014cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal                float ty = 0;
5024cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal                for (int j = 0; j < spanY; j++, ty += tileH) {
5034cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal                    dst.offsetTo(tx, ty);
5044cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal                    c.drawBitmap(tileBitmap, src, dst, p);
5054cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal                }
50605713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            }
5074cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal
5084cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal            // Draw the icon in the top left corner
5094cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal            // TODO: use top right for RTL
5104cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal            int minOffset = (int) (mAppIconSize * WIDGET_PREVIEW_ICON_PADDING_PERCENTAGE);
5114cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal            int smallestSide = Math.min(previewWidth, previewHeight);
5124cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal            float iconScale = Math.min((float) smallestSide / (mAppIconSize + 2 * minOffset), scale);
5134cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal
5144cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal            try {
5154cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal                Drawable icon = mutateOnMainThread(mManager.loadIcon(info, mIconCache));
5164cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal                if (icon != null) {
5174cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal                    int hoffset = (int) ((tileW - mAppIconSize * iconScale) / 2) + x;
5184cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal                    int yoffset = (int) ((tileH - mAppIconSize * iconScale) / 2);
5194cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal                    icon.setBounds(hoffset, yoffset,
5204cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal                            hoffset + (int) (mAppIconSize * iconScale),
5214cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal                            yoffset + (int) (mAppIconSize * iconScale));
5224cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal                    icon.draw(c);
5234cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal                }
5244cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal            } catch (Resources.NotFoundException e) { }
52505713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            c.setBitmap(null);
52605713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        }
527ffe83f13319cdb833a25596d47cf1af098cc4c60Sunny Goyal        return mManager.getBadgeBitmap(info, preview);
52805713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka    }
52905713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka
53005713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka    private Bitmap generateShortcutPreview(
53105713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            ResolveInfo info, int maxWidth, int maxHeight, Bitmap preview) {
5324cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal        final Canvas c = new Canvas();
5334cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal        if (preview == null) {
5344cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal            preview = Bitmap.createBitmap(maxWidth, maxHeight, Config.ARGB_8888);
5354cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal            c.setBitmap(preview);
5364cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal        } else if (preview.getWidth() != maxWidth || preview.getHeight() != maxHeight) {
5374cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal            throw new RuntimeException("Improperly sized bitmap passed as argument");
53805713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        } else {
5394cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal            // Reusing bitmap. Clear it.
5404cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal            c.setBitmap(preview);
54105713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka            c.drawColor(0, PorterDuff.Mode.CLEAR);
54205713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        }
54305713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka
5444cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal        Drawable icon = mutateOnMainThread(mIconCache.getFullResIcon(info.activityInfo));
5454cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal        icon.setFilterBitmap(true);
54605713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka
5474cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal        // Draw a desaturated/scaled version of the icon in the background as a watermark
5484cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal        ColorMatrix colorMatrix = new ColorMatrix();
5494cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal        colorMatrix.setSaturation(0);
5504cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal        icon.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
5514cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal        icon.setAlpha((int) (255 * 0.06f));
5524cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal
5534cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal        Resources res = mContext.getResources();
5544cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal        int paddingTop = res.getDimensionPixelOffset(R.dimen.shortcut_preview_padding_top);
5554cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal        int paddingLeft = res.getDimensionPixelOffset(R.dimen.shortcut_preview_padding_left);
5564cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal        int paddingRight = res.getDimensionPixelOffset(R.dimen.shortcut_preview_padding_right);
55705713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        int scaledIconWidth = (maxWidth - paddingLeft - paddingRight);
5584cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal        icon.setBounds(paddingLeft, paddingTop,
5594cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal                paddingLeft + scaledIconWidth, paddingTop + scaledIconWidth);
5604cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal        icon.draw(c);
56105713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka
5624cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal        // Draw the final icon at top left corner.
5634cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal        // TODO: use top right for RTL
5644cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal        icon.setAlpha(255);
5654cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal        icon.setColorFilter(null);
5664cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal        icon.setBounds(0, 0, mAppIconSize, mAppIconSize);
5674cad7538c9615303d291f5b52e960aaf0985828fSunny Goyal        icon.draw(c);
56805713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka
56905713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        c.setBitmap(null);
57005713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka        return preview;
57105713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka    }
57205713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka
57365d60e21ec22e3bf03ba39f7a0be24df2cc914a2Adrian Roos    private Drawable mutateOnMainThread(final Drawable drawable) {
57465d60e21ec22e3bf03ba39f7a0be24df2cc914a2Adrian Roos        try {
57565d60e21ec22e3bf03ba39f7a0be24df2cc914a2Adrian Roos            return mMainThreadExecutor.submit(new Callable<Drawable>() {
57665d60e21ec22e3bf03ba39f7a0be24df2cc914a2Adrian Roos                @Override
57765d60e21ec22e3bf03ba39f7a0be24df2cc914a2Adrian Roos                public Drawable call() throws Exception {
57865d60e21ec22e3bf03ba39f7a0be24df2cc914a2Adrian Roos                    return drawable.mutate();
57965d60e21ec22e3bf03ba39f7a0be24df2cc914a2Adrian Roos                }
58065d60e21ec22e3bf03ba39f7a0be24df2cc914a2Adrian Roos            }).get();
58165d60e21ec22e3bf03ba39f7a0be24df2cc914a2Adrian Roos        } catch (InterruptedException e) {
58265d60e21ec22e3bf03ba39f7a0be24df2cc914a2Adrian Roos            Thread.currentThread().interrupt();
58365d60e21ec22e3bf03ba39f7a0be24df2cc914a2Adrian Roos            throw new RuntimeException(e);
58465d60e21ec22e3bf03ba39f7a0be24df2cc914a2Adrian Roos        } catch (ExecutionException e) {
58565d60e21ec22e3bf03ba39f7a0be24df2cc914a2Adrian Roos            throw new RuntimeException(e);
58665d60e21ec22e3bf03ba39f7a0be24df2cc914a2Adrian Roos        }
58765d60e21ec22e3bf03ba39f7a0be24df2cc914a2Adrian Roos    }
5881f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos
5891f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos    private static final int MAX_OPEN_FILES = 1024;
5901f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos    private static final int SAMPLE_RATE = 23;
5911f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos    /**
5921f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos     * Dumps all files that are open in this process without allocating a file descriptor.
5931f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos     */
594091440a9cb9d4f42406631004aa484cbb79214caAdam Cohen    @Thunk static void dumpOpenFiles() {
5951f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos        try {
5961f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos            Log.i(TAG, "DUMP OF OPEN FILES (sample rate: 1 every " + SAMPLE_RATE + "):");
5971f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos            final String TYPE_APK = "apk";
5981f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos            final String TYPE_JAR = "jar";
5991f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos            final String TYPE_PIPE = "pipe";
6001f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos            final String TYPE_SOCKET = "socket";
6011f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos            final String TYPE_DB = "db";
6021f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos            final String TYPE_ANON_INODE = "anon_inode";
6031f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos            final String TYPE_DEV = "dev";
6041f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos            final String TYPE_NON_FS = "non-fs";
6051f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos            final String TYPE_OTHER = "other";
6061f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos            List<String> types = Arrays.asList(TYPE_APK, TYPE_JAR, TYPE_PIPE, TYPE_SOCKET, TYPE_DB,
6071f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                    TYPE_ANON_INODE, TYPE_DEV, TYPE_NON_FS, TYPE_OTHER);
6081f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos            int[] count = new int[types.size()];
6091f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos            int[] duplicates = new int[types.size()];
6101f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos            HashSet<String> files = new HashSet<String>();
6111f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos            int total = 0;
6121f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos            for (int i = 0; i < MAX_OPEN_FILES; i++) {
6131f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                // This is a gigantic hack but unfortunately the only way to resolve an fd
6141f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                // to a file name. Note that we have to loop over all possible fds because
6151f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                // reading the directory would require allocating a new fd. The kernel is
6161f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                // currently implemented such that no fd is larger then the current rlimit,
6171f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                // which is why it's safe to loop over them in such a way.
6181f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                String fd = "/proc/self/fd/" + i;
6191f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                try {
6201f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                    // getCanonicalPath() uses readlink behind the scene which doesn't require
6211f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                    // a file descriptor.
6221f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                    String resolved = new File(fd).getCanonicalPath();
6231f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                    int type = types.indexOf(TYPE_OTHER);
6241f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                    if (resolved.startsWith("/dev/")) {
6251f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                        type = types.indexOf(TYPE_DEV);
6261f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                    } else if (resolved.endsWith(".apk")) {
6271f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                        type = types.indexOf(TYPE_APK);
6281f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                    } else if (resolved.endsWith(".jar")) {
6291f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                        type = types.indexOf(TYPE_JAR);
6301f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                    } else if (resolved.contains("/fd/pipe:")) {
6311f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                        type = types.indexOf(TYPE_PIPE);
6321f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                    } else if (resolved.contains("/fd/socket:")) {
6331f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                        type = types.indexOf(TYPE_SOCKET);
6341f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                    } else if (resolved.contains("/fd/anon_inode:")) {
6351f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                        type = types.indexOf(TYPE_ANON_INODE);
6361f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                    } else if (resolved.endsWith(".db") || resolved.contains("/databases/")) {
6371f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                        type = types.indexOf(TYPE_DB);
6381f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                    } else if (resolved.startsWith("/proc/") && resolved.contains("/fd/")) {
6391f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                        // Those are the files that don't point anywhere on the file system.
6401f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                        // getCanonicalPath() wrongly interprets these as relative symlinks and
6411f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                        // resolves them within /proc/<pid>/fd/.
6421f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                        type = types.indexOf(TYPE_NON_FS);
6431f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                    }
6441f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                    count[type]++;
6451f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                    total++;
6461f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                    if (files.contains(resolved)) {
6471f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                        duplicates[type]++;
6481f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                    }
6491f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                    files.add(resolved);
6501f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                    if (total % SAMPLE_RATE == 0) {
6511f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                        Log.i(TAG, " fd " + i + ": " + resolved
6521f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                                + " (" + types.get(type) + ")");
6531f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                    }
6541f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                } catch (IOException e) {
6551f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                    // Ignoring exceptions for non-existing file descriptors.
6561f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                }
6571f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos            }
6581f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos            for (int i = 0; i < types.size(); i++) {
6591f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                Log.i(TAG, String.format("Open %10s files: %4d total, %4d duplicates",
6601f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos                        types.get(i), count[i], duplicates[i]));
6611f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos            }
6621f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos        } catch (Throwable t) {
6631f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos            // Catch everything. This is called from an exception handler that we shouldn't upset.
6641f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos            Log.e(TAG, "Unable to log open files.", t);
6651f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos        }
6661f375ab63339359e1f8cd44cede466adedcdcc62Adrian Roos    }
66705713af127d765cc28a8b2fd548a90347c90d6cbMichael Jurka}
668