MiniThumbFile.java revision ef093cd6c4ab4d3c8a1c8be5ed7147d5f06d7027
100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen/*
200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * Copyright (C) 2009 The Android Open Source Project
300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen *
400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * Licensed under the Apache License, Version 2.0 (the "License");
500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * you may not use this file except in compliance with the License.
600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * You may obtain a copy of the License at
700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen *
800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen *      http://www.apache.org/licenses/LICENSE-2.0
900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen *
1000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * Unless required by applicable law or agreed to in writing, software
1100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * distributed under the License is distributed on an "AS IS" BASIS,
1200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * See the License for the specific language governing permissions and
1400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * limitations under the License.
1500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen */
1600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen
1700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chenpackage android.media;
1800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen
1900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chenimport android.graphics.Bitmap;
2000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chenimport android.media.ThumbnailUtil;
2100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chenimport android.net.Uri;
2200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chenimport android.os.Environment;
2300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chenimport android.util.Log;
2400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen
2500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chenimport java.io.File;
2600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chenimport java.io.IOException;
2700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chenimport java.io.RandomAccessFile;
28ecc97cc9491f40c507e372a30bcd58a9e08066a8Ray Chenimport java.nio.ByteBuffer;
2900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chenimport java.nio.channels.FileChannel;
3000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chenimport java.nio.channels.FileLock;
3100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chenimport java.util.Hashtable;
3200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen
3300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen/**
3400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * This class handles the mini-thumb file. A mini-thumb file consists
3500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * of blocks, indexed by id. Each block has BYTES_PER_MINTHUMB bytes in the
3600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * following format:
3700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen *
3800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * 1 byte status (0 = empty, 1 = mini-thumb available)
3900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * 8 bytes magic (a magic number to match what's in the database)
4000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * 4 bytes data length (LEN)
4100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * LEN bytes jpeg data
4200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * (the remaining bytes are unused)
4300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen *
4400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * @hide This file is shared between MediaStore and MediaProvider and should remained internal use
4500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen *       only.
4600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen */
4700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chenpublic class MiniThumbFile {
4800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen    private static final String TAG = "MiniThumbFile";
4900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen    private static final int MINI_THUMB_DATA_FILE_VERSION = 3;
5000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen    public static final int BYTES_PER_MINTHUMB = 10000;
5100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen    private static final int HEADER_SIZE = 1 + 8 + 4;
5200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen    private Uri mUri;
5300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen    private RandomAccessFile mMiniThumbFile;
5400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen    private FileChannel mChannel;
55ecc97cc9491f40c507e372a30bcd58a9e08066a8Ray Chen    private ByteBuffer mBuffer;
5600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen    private static Hashtable<String, MiniThumbFile> sThumbFiles =
5700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen        new Hashtable<String, MiniThumbFile>();
5800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen
5900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen    /**
6000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen     * We store different types of thumbnails in different files. To remain backward compatibility,
6100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen     * we should hashcode of content://media/external/images/media remains the same.
6200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen     */
6300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen    public static synchronized void reset() {
64ef093cd6c4ab4d3c8a1c8be5ed7147d5f06d7027Ray Chen        for (MiniThumbFile file : sThumbFiles.values()) {
65ef093cd6c4ab4d3c8a1c8be5ed7147d5f06d7027Ray Chen            file.deactivate();
66ef093cd6c4ab4d3c8a1c8be5ed7147d5f06d7027Ray Chen        }
6700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen        sThumbFiles.clear();
6800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen    }
6900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen
7000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen    public static synchronized MiniThumbFile instance(Uri uri) {
7100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen        String type = uri.getPathSegments().get(1);
7200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen        MiniThumbFile file = sThumbFiles.get(type);
7300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen        // Log.v(TAG, "get minithumbfile for type: "+type);
7400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen        if (file == null) {
7500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            file = new MiniThumbFile(
7600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen                    Uri.parse("content://media/external/" + type + "/media"));
7700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            sThumbFiles.put(type, file);
7800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen        }
7900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen
8000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen        return file;
8100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen    }
8200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen
8300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen    private String randomAccessFilePath(int version) {
8400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen        String directoryName =
8500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen                Environment.getExternalStorageDirectory().toString()
8600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen                + "/DCIM/.thumbnails";
8700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen        return directoryName + "/.thumbdata" + version + "-" + mUri.hashCode();
8800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen    }
8900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen
9000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen    private void removeOldFile() {
9100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen        String oldPath = randomAccessFilePath(MINI_THUMB_DATA_FILE_VERSION - 1);
9200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen        File oldFile = new File(oldPath);
9300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen        if (oldFile.exists()) {
9400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            try {
9500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen                oldFile.delete();
9600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            } catch (SecurityException ex) {
9700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen                // ignore
9800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            }
9900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen        }
10000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen    }
10100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen
10200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen    private RandomAccessFile miniThumbDataFile() {
10300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen        if (mMiniThumbFile == null) {
10400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            removeOldFile();
10500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            String path = randomAccessFilePath(MINI_THUMB_DATA_FILE_VERSION);
10600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            File directory = new File(path).getParentFile();
10700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            if (!directory.isDirectory()) {
10800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen                if (!directory.mkdirs()) {
10900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen                    Log.e(TAG, "Unable to create .thumbnails directory "
11000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen                            + directory.toString());
11100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen                }
11200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            }
11300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            File f = new File(path);
11400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            try {
11500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen                mMiniThumbFile = new RandomAccessFile(f, "rw");
11600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            } catch (IOException ex) {
11700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen                // Open as read-only so we can at least read the existing
11800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen                // thumbnails.
11900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen                try {
12000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen                    mMiniThumbFile = new RandomAccessFile(f, "r");
12100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen                } catch (IOException ex2) {
12200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen                    // ignore exception
12300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen                }
12400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            }
125f5722b3f22e016a8f21c628776cd0746ef164c19Chih-Chung Chang            if (mMiniThumbFile != null) {
126f5722b3f22e016a8f21c628776cd0746ef164c19Chih-Chung Chang                mChannel = mMiniThumbFile.getChannel();
127f5722b3f22e016a8f21c628776cd0746ef164c19Chih-Chung Chang            }
12800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen        }
12900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen        return mMiniThumbFile;
13000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen    }
13100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen
13200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen    public MiniThumbFile(Uri uri) {
13300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen        mUri = uri;
134ecc97cc9491f40c507e372a30bcd58a9e08066a8Ray Chen        mBuffer = ByteBuffer.allocateDirect(BYTES_PER_MINTHUMB);
13500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen    }
13600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen
13700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen    public synchronized void deactivate() {
13800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen        if (mMiniThumbFile != null) {
13900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            try {
14000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen                mMiniThumbFile.close();
14100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen                mMiniThumbFile = null;
14200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            } catch (IOException ex) {
14300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen                // ignore exception
14400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            }
14500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen        }
14600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen    }
14700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen
14800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen    // Get the magic number for the specified id in the mini-thumb file.
14900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen    // Returns 0 if the magic is not available.
150ef093cd6c4ab4d3c8a1c8be5ed7147d5f06d7027Ray Chen    public synchronized long getMagic(long id) {
15100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen        // check the mini thumb file for the right data.  Right is
15200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen        // defined as having the right magic number at the offset
15300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen        // reserved for this "id".
15400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen        RandomAccessFile r = miniThumbDataFile();
15500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen        if (r != null) {
15600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            long pos = id * BYTES_PER_MINTHUMB;
15700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            FileLock lock = null;
15800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            try {
159ecc97cc9491f40c507e372a30bcd58a9e08066a8Ray Chen                mBuffer.clear();
160ecc97cc9491f40c507e372a30bcd58a9e08066a8Ray Chen                mBuffer.limit(1 + 8);
161ecc97cc9491f40c507e372a30bcd58a9e08066a8Ray Chen
162ecc97cc9491f40c507e372a30bcd58a9e08066a8Ray Chen                lock = mChannel.lock(pos, 1 + 8, true);
16300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen                // check that we can read the following 9 bytes
16400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen                // (1 for the "status" and 8 for the long)
165ecc97cc9491f40c507e372a30bcd58a9e08066a8Ray Chen                if (mChannel.read(mBuffer, pos) == 9) {
166ecc97cc9491f40c507e372a30bcd58a9e08066a8Ray Chen                    mBuffer.position(0);
167ecc97cc9491f40c507e372a30bcd58a9e08066a8Ray Chen                    if (mBuffer.get() == 1) {
168ecc97cc9491f40c507e372a30bcd58a9e08066a8Ray Chen                        return mBuffer.getLong();
16900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen                    }
17000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen                }
17100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            } catch (IOException ex) {
17200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen                Log.v(TAG, "Got exception checking file magic: ", ex);
17300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            } catch (RuntimeException ex) {
17400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen                // Other NIO related exception like disk full, read only channel..etc
17500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen                Log.e(TAG, "Got exception when reading magic, id = " + id +
17600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen                        ", disk full or mount read-only? " + ex.getClass());
17700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            } finally {
17800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen                try {
17900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen                    if (lock != null) lock.release();
18000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen                }
18100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen                catch (IOException ex) {
18200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen                    // ignore it.
18300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen                }
18400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            }
18500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen        }
18600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen        return 0;
18700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen    }
18800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen
189ef093cd6c4ab4d3c8a1c8be5ed7147d5f06d7027Ray Chen    public synchronized void saveMiniThumbToFile(byte[] data, long id, long magic)
19000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            throws IOException {
19100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen        RandomAccessFile r = miniThumbDataFile();
19200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen        if (r == null) return;
19300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen
19400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen        long pos = id * BYTES_PER_MINTHUMB;
19500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen        FileLock lock = null;
19600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen        try {
19700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            if (data != null) {
19800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen                if (data.length > BYTES_PER_MINTHUMB - HEADER_SIZE) {
19900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen                    // not enough space to store it.
20000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen                    return;
20100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen                }
202ecc97cc9491f40c507e372a30bcd58a9e08066a8Ray Chen                mBuffer.clear();
203ecc97cc9491f40c507e372a30bcd58a9e08066a8Ray Chen                mBuffer.put((byte) 1);
204ecc97cc9491f40c507e372a30bcd58a9e08066a8Ray Chen                mBuffer.putLong(magic);
205ecc97cc9491f40c507e372a30bcd58a9e08066a8Ray Chen                mBuffer.putInt(data.length);
206ecc97cc9491f40c507e372a30bcd58a9e08066a8Ray Chen                mBuffer.put(data);
207ecc97cc9491f40c507e372a30bcd58a9e08066a8Ray Chen                mBuffer.flip();
20800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen
209ecc97cc9491f40c507e372a30bcd58a9e08066a8Ray Chen                lock = mChannel.lock(pos, BYTES_PER_MINTHUMB, false);
210ecc97cc9491f40c507e372a30bcd58a9e08066a8Ray Chen                mChannel.write(mBuffer, pos);
21100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            }
21200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen        } catch (IOException ex) {
21300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            Log.e(TAG, "couldn't save mini thumbnail data for "
21400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen                    + id + "; ", ex);
21500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            throw ex;
21600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen        } catch (RuntimeException ex) {
21700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            // Other NIO related exception like disk full, read only channel..etc
21800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            Log.e(TAG, "couldn't save mini thumbnail data for "
21900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen                    + id + "; disk full or mount read-only? " + ex.getClass());
22000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen        } finally {
22100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            try {
22200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen                if (lock != null) lock.release();
22300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            }
22400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            catch (IOException ex) {
22500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen                // ignore it.
22600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            }
22700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen        }
22800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen    }
22900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen
23000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen    /**
23100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen     * Gallery app can use this method to retrieve mini-thumbnail. Full size
23200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen     * images share the same IDs with their corresponding thumbnails.
23300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen     *
23400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen     * @param id the ID of the image (same of full size image).
23500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen     * @param data the buffer to store mini-thumbnail.
23600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen     */
237ef093cd6c4ab4d3c8a1c8be5ed7147d5f06d7027Ray Chen    public synchronized byte [] getMiniThumbFromFile(long id, byte [] data) {
23800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen        RandomAccessFile r = miniThumbDataFile();
23900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen        if (r == null) return null;
24000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen
24100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen        long pos = id * BYTES_PER_MINTHUMB;
24200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen        FileLock lock = null;
24300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen        try {
244ecc97cc9491f40c507e372a30bcd58a9e08066a8Ray Chen            mBuffer.clear();
245ecc97cc9491f40c507e372a30bcd58a9e08066a8Ray Chen            lock = mChannel.lock(pos, BYTES_PER_MINTHUMB, true);
246ecc97cc9491f40c507e372a30bcd58a9e08066a8Ray Chen            int size = mChannel.read(mBuffer, pos);
247ecc97cc9491f40c507e372a30bcd58a9e08066a8Ray Chen            if (size > 1 + 8 + 4) { // flag, magic, length
248ecc97cc9491f40c507e372a30bcd58a9e08066a8Ray Chen                mBuffer.position(0);
249ecc97cc9491f40c507e372a30bcd58a9e08066a8Ray Chen                byte flag = mBuffer.get();
250ecc97cc9491f40c507e372a30bcd58a9e08066a8Ray Chen                long magic = mBuffer.getLong();
251ecc97cc9491f40c507e372a30bcd58a9e08066a8Ray Chen                int length = mBuffer.getInt();
252ecc97cc9491f40c507e372a30bcd58a9e08066a8Ray Chen
253ecc97cc9491f40c507e372a30bcd58a9e08066a8Ray Chen                if (size >= 1 + 8 + 4 + length && data.length >= length) {
254ecc97cc9491f40c507e372a30bcd58a9e08066a8Ray Chen                    mBuffer.get(data, 0, length);
255ecc97cc9491f40c507e372a30bcd58a9e08066a8Ray Chen                    return data;
256ecc97cc9491f40c507e372a30bcd58a9e08066a8Ray Chen                }
25700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            }
25800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen        } catch (IOException ex) {
259ecc97cc9491f40c507e372a30bcd58a9e08066a8Ray Chen            Log.w(TAG, "got exception when reading thumbnail id=" + id + ", exception: " + ex);
26000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen        } catch (RuntimeException ex) {
26100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            // Other NIO related exception like disk full, read only channel..etc
26200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            Log.e(TAG, "Got exception when reading thumbnail, id = " + id +
26300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen                    ", disk full or mount read-only? " + ex.getClass());
26400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen        } finally {
26500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            try {
26600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen                if (lock != null) lock.release();
26700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            }
26800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            catch (IOException ex) {
26900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen                // ignore it.
27000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            }
27100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen        }
27200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen        return null;
27300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen    }
27400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen}
275