MediaScanner.java revision 250528e5b2362cb7b6749afb331acd8226f67744
19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2007 The Android Open Source Project
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License.
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.media;
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
19bfcb22a207499c7ecd4aef9bea2ac778d8be8fc8Marco Nelissenimport org.xml.sax.Attributes;
20bfcb22a207499c7ecd4aef9bea2ac778d8be8fc8Marco Nelissenimport org.xml.sax.ContentHandler;
21bfcb22a207499c7ecd4aef9bea2ac778d8be8fc8Marco Nelissenimport org.xml.sax.SAXException;
22bfcb22a207499c7ecd4aef9bea2ac778d8be8fc8Marco Nelissen
23bfcb22a207499c7ecd4aef9bea2ac778d8be8fc8Marco Nelissenimport android.content.ContentUris;
249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.ContentValues;
259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.Context;
269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.IContentProvider;
279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.database.Cursor;
289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.database.SQLException;
295d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wangimport android.drm.DrmManagerClient;
309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.BitmapFactory;
319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.net.Uri;
32250528e5b2362cb7b6749afb331acd8226f67744Mike Lockwoodimport android.os.Environment;
339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.Process;
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.RemoteException;
359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.SystemProperties;
369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.provider.MediaStore;
379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.provider.Settings;
389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.provider.MediaStore.Audio;
39c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwoodimport android.provider.MediaStore.Files;
409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.provider.MediaStore.Images;
419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.provider.MediaStore.Video;
429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.provider.MediaStore.Audio.Genres;
439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.provider.MediaStore.Audio.Playlists;
449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.sax.Element;
459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.sax.ElementListener;
469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.sax.RootElement;
479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.TextUtils;
489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.Config;
499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.Log;
509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.Xml;
519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
52bfcb22a207499c7ecd4aef9bea2ac778d8be8fc8Marco Nelissenimport java.io.BufferedReader;
53bfcb22a207499c7ecd4aef9bea2ac778d8be8fc8Marco Nelissenimport java.io.File;
54bfcb22a207499c7ecd4aef9bea2ac778d8be8fc8Marco Nelissenimport java.io.FileDescriptor;
55bfcb22a207499c7ecd4aef9bea2ac778d8be8fc8Marco Nelissenimport java.io.FileInputStream;
56bfcb22a207499c7ecd4aef9bea2ac778d8be8fc8Marco Nelissenimport java.io.IOException;
57bfcb22a207499c7ecd4aef9bea2ac778d8be8fc8Marco Nelissenimport java.io.InputStreamReader;
589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.ArrayList;
599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.HashMap;
609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.HashSet;
619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.Iterator;
629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
6477f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * Internal service helper that no-one should use directly.
6520b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync *
6677f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * The way the scan currently works is:
6777f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * - The Java MediaScannerService creates a MediaScanner (this class), and calls
6877f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen *   MediaScanner.scanDirectories on it.
6977f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * - scanDirectories() calls the native processDirectory() for each of the specified directories.
7077f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * - the processDirectory() JNI method wraps the provided mediascanner client in a native
7177f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen *   'MyMediaScannerClient' class, then calls processDirectory() on the native MediaScanner
7277f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen *   object (which got created when the Java MediaScanner was created).
7377f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * - native MediaScanner.processDirectory() (currently part of opencore) calls
7477f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen *   doProcessDirectory(), which recurses over the folder, and calls
7577f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen *   native MyMediaScannerClient.scanFile() for every file whose extension matches.
7677f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * - native MyMediaScannerClient.scanFile() calls back on Java MediaScannerClient.scanFile,
7777f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen *   which calls doScanFile, which after some setup calls back down to native code, calling
7877f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen *   MediaScanner.processFile().
7977f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * - MediaScanner.processFile() calls one of several methods, depending on the type of the
8077f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen *   file: parseMP3, parseMP4, parseMidi, parseOgg or parseWMA.
8177f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * - each of these methods gets metadata key/value pairs from the file, and repeatedly
8277f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen *   calls native MyMediaScannerClient.handleStringTag, which calls back up to its Java
8377f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen *   counterparts in this file.
8477f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * - Java handleStringTag() gathers the key/value pairs that it's interested in.
8577f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * - once processFile returns and we're back in Java code in doScanFile(), it calls
8677f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen *   Java MyMediaScannerClient.endFile(), which takes all the data that's been
8777f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen *   gathered and inserts an entry in to the database.
8877f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen *
8977f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * In summary:
9077f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * Java MediaScannerService calls
9177f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * Java MediaScanner scanDirectories, which calls
9277f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * Java MediaScanner processDirectory (native method), which calls
9377f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * native MediaScanner processDirectory, which calls
9477f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * native MyMediaScannerClient scanFile, which calls
9577f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * Java MyMediaScannerClient scanFile, which calls
9677f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * Java MediaScannerClient doScanFile, which calls
9777f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * Java MediaScanner processFile (native method), which calls
9877f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * native MediaScanner processFile, which calls
9977f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * native parseMP3, parseMP4, parseMidi, parseOgg or parseWMA, which calls
10077f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * native MyMediaScanner handleStringTag, which calls
10177f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * Java MyMediaScanner handleStringTag.
10277f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * Once MediaScanner processFile returns, an entry is inserted in to the database.
1039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
1049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * {@hide}
1059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
1069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic class MediaScanner
10720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync{
1089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static {
1099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        System.loadLibrary("media_jni");
1104935d05eaa306cef88cf0ab13eca386f270409ecMarco Nelissen        native_init();
1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private final static String TAG = "MediaScanner";
1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
115d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood    private static final String[] FILES_PRESCAN_PROJECTION = new String[] {
116c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood            Files.FileColumns._ID, // 0
117c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood            Files.FileColumns.DATA, // 1
118d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood            Files.FileColumns.FORMAT, // 2
119d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood            Files.FileColumns.DATE_MODIFIED, // 3
1209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    };
12120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
1226458935331ccfacb4cccc50d6b893947febae687Mike Lockwood    private static final String[] ID_PROJECTION = new String[] {
1236458935331ccfacb4cccc50d6b893947febae687Mike Lockwood            Files.FileColumns._ID,
1246458935331ccfacb4cccc50d6b893947febae687Mike Lockwood    };
1256458935331ccfacb4cccc50d6b893947febae687Mike Lockwood
126d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood    private static final int FILES_PRESCAN_ID_COLUMN_INDEX = 0;
127d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood    private static final int FILES_PRESCAN_PATH_COLUMN_INDEX = 1;
128d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood    private static final int FILES_PRESCAN_FORMAT_COLUMN_INDEX = 2;
129d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood    private static final int FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX = 3;
130d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood
1319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String[] PLAYLIST_MEMBERS_PROJECTION = new String[] {
1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Audio.Playlists.Members.PLAYLIST_ID, // 0
1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     };
1349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int ID_PLAYLISTS_COLUMN_INDEX = 0;
1369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int PATH_PLAYLISTS_COLUMN_INDEX = 1;
1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int DATE_MODIFIED_PLAYLISTS_COLUMN_INDEX = 2;
1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String[] GENRE_LOOKUP_PROJECTION = new String[] {
1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Audio.Genres._ID, // 0
1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Audio.Genres.NAME, // 1
1429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    };
1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String RINGTONES_DIR = "/ringtones/";
1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String NOTIFICATIONS_DIR = "/notifications/";
1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String ALARMS_DIR = "/alarms/";
1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String MUSIC_DIR = "/music/";
1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String PODCAST_DIR = "/podcasts/";
14920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String[] ID3_GENRES = {
1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // ID3v1 Genres
1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Blues",
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Classic Rock",
1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Country",
1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Dance",
1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Disco",
1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Funk",
1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Grunge",
1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Hip-Hop",
1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Jazz",
1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Metal",
1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "New Age",
1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Oldies",
1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Other",
1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Pop",
1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "R&B",
1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Rap",
1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Reggae",
1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Rock",
1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Techno",
1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Industrial",
1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Alternative",
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Ska",
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Death Metal",
1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Pranks",
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Soundtrack",
1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Euro-Techno",
1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Ambient",
1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Trip-Hop",
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Vocal",
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Jazz+Funk",
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Fusion",
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Trance",
1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Classical",
1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Instrumental",
1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Acid",
1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "House",
1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Game",
1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Sound Clip",
1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Gospel",
1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Noise",
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "AlternRock",
1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Bass",
1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Soul",
1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Punk",
1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Space",
1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Meditative",
1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Instrumental Pop",
1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Instrumental Rock",
2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Ethnic",
2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Gothic",
2029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Darkwave",
2039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Techno-Industrial",
2049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Electronic",
2059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Pop-Folk",
2069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Eurodance",
2079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Dream",
2089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Southern Rock",
2099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Comedy",
2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Cult",
2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Gangsta",
2129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Top 40",
2139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Christian Rap",
2149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Pop/Funk",
2159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Jungle",
2169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Native American",
2179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Cabaret",
2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "New Wave",
2199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Psychadelic",
2209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Rave",
2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Showtunes",
2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Trailer",
2239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Lo-Fi",
2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Tribal",
2259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Acid Punk",
2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Acid Jazz",
2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Polka",
2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Retro",
2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Musical",
2309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Rock & Roll",
2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Hard Rock",
2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // The following genres are Winamp extensions
2339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Folk",
2349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Folk-Rock",
2359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "National Folk",
2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Swing",
2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Fast Fusion",
2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Bebob",
2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Latin",
2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Revival",
2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Celtic",
2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Bluegrass",
2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Avantgarde",
2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Gothic Rock",
2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Progressive Rock",
2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Psychedelic Rock",
2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Symphonic Rock",
2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Slow Rock",
2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Big Band",
2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Chorus",
2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Easy Listening",
2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Acoustic",
2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Humour",
2549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Speech",
2559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Chanson",
2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Opera",
2579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Chamber Music",
2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Sonata",
2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Symphony",
2609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Booty Bass",
2619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Primus",
2629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Porn Groove",
2639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Satire",
2649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Slow Jam",
2659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Club",
2669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Tango",
2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Samba",
2689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Folklore",
2699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Ballad",
2709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Power Ballad",
2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Rhythmic Soul",
2729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Freestyle",
2739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Duet",
2749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Punk Rock",
2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Drum Solo",
2769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "A capella",
2779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Euro-House",
2789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Dance Hall"
2799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    };
2809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mNativeContext;
2829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Context mContext;
2839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private IContentProvider mMediaProvider;
2849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Uri mAudioUri;
2859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Uri mVideoUri;
2869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Uri mImagesUri;
2879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Uri mThumbsUri;
2889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Uri mGenresUri;
2899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Uri mPlaylistsUri;
290c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood    private Uri mFilesUri;
2919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean mProcessPlaylists, mProcessGenres;
292d815f79766984fce499e147ecbacc01914683f74Mike Lockwood    private int mMtpObjectHandle;
2939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
294250528e5b2362cb7b6749afb331acd8226f67744Mike Lockwood    private final String mMediaStoragePath;
295250528e5b2362cb7b6749afb331acd8226f67744Mike Lockwood    private final String mExternalStoragePath;
296250528e5b2362cb7b6749afb331acd8226f67744Mike Lockwood
2979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // used when scanning the image database so we know whether we have to prune
2989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // old thumbnail files
2999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mOriginalCount;
3009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** Whether the scanner has set a default sound for the ringer ringtone. */
3019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean mDefaultRingtoneSet;
3029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** Whether the scanner has set a default sound for the notification ringtone. */
3039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean mDefaultNotificationSet;
3043156bb00dd3ab3d6ec732bb6a64a2e84d08c34f0Patrick Scott    /** Whether the scanner has set a default sound for the alarm ringtone. */
3053156bb00dd3ab3d6ec732bb6a64a2e84d08c34f0Patrick Scott    private boolean mDefaultAlarmSet;
3069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** The filename for the default sound for the ringer ringtone. */
3079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private String mDefaultRingtoneFilename;
3089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** The filename for the default sound for the notification ringtone. */
3099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private String mDefaultNotificationFilename;
3103156bb00dd3ab3d6ec732bb6a64a2e84d08c34f0Patrick Scott    /** The filename for the default sound for the alarm ringtone. */
3113156bb00dd3ab3d6ec732bb6a64a2e84d08c34f0Patrick Scott    private String mDefaultAlarmAlertFilename;
3129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * The prefix for system properties that define the default sound for
3149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * ringtones. Concatenate the name of the setting from Settings
3159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * to get the full system property.
3169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String DEFAULT_RINGTONE_PROPERTY_PREFIX = "ro.config.";
31820b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
3199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // set to true if file path comparisons should be case insensitive.
3209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // this should be set when scanning files on a case insensitive file system.
3219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean mCaseInsensitivePaths;
32220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
3239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private BitmapFactory.Options mBitmapOptions = new BitmapFactory.Options();
3249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static class FileCacheEntry {
3269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Uri mTableUri;
3279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        long mRowId;
3289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String mPath;
3299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        long mLastModified;
330d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood        int mFormat;
3319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean mSeenInFileSystem;
3329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean mLastModifiedChanged;
33320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
334d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood        FileCacheEntry(Uri tableUri, long rowId, String path, long lastModified, int format) {
3359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mTableUri = tableUri;
3369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mRowId = rowId;
3379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mPath = path;
3389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mLastModified = lastModified;
339d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood            mFormat = format;
3409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mSeenInFileSystem = false;
3419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mLastModifiedChanged = false;
3429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        @Override
3459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String toString() {
346c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood            return mPath + " mTableUri: " + mTableUri + " mRowId: " + mRowId;
3479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
34920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
35020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync    // hashes file path to FileCacheEntry.
3519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // path should be lower case if mCaseInsensitivePaths is true
35220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync    private HashMap<String, FileCacheEntry> mFileCache;
3539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private ArrayList<FileCacheEntry> mPlayLists;
3559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private HashMap<String, Uri> mGenreCache;
3569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3575d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang    private DrmManagerClient mDrmManagerClient = null;
3589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public MediaScanner(Context c) {
3609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        native_setup();
3619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mContext = c;
3629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mBitmapOptions.inSampleSize = 1;
3639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mBitmapOptions.inJustDecodeBounds = true;
36420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
3659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        setDefaultRingtoneFileNames();
366250528e5b2362cb7b6749afb331acd8226f67744Mike Lockwood
367250528e5b2362cb7b6749afb331acd8226f67744Mike Lockwood        String mediaStoragePath = SystemProperties.get("ro.media.storage");
368250528e5b2362cb7b6749afb331acd8226f67744Mike Lockwood        if (mediaStoragePath != null &&  mediaStoragePath.length() > 0) {
369250528e5b2362cb7b6749afb331acd8226f67744Mike Lockwood            mMediaStoragePath = mediaStoragePath;
370250528e5b2362cb7b6749afb331acd8226f67744Mike Lockwood        } else {
371250528e5b2362cb7b6749afb331acd8226f67744Mike Lockwood            mMediaStoragePath = null;
372250528e5b2362cb7b6749afb331acd8226f67744Mike Lockwood        }
373250528e5b2362cb7b6749afb331acd8226f67744Mike Lockwood        mExternalStoragePath = Environment.getExternalStorageDirectory().getAbsolutePath();
3749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void setDefaultRingtoneFileNames() {
3779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mDefaultRingtoneFilename = SystemProperties.get(DEFAULT_RINGTONE_PROPERTY_PREFIX
3789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                + Settings.System.RINGTONE);
3799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mDefaultNotificationFilename = SystemProperties.get(DEFAULT_RINGTONE_PROPERTY_PREFIX
3809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                + Settings.System.NOTIFICATION_SOUND);
3813156bb00dd3ab3d6ec732bb6a64a2e84d08c34f0Patrick Scott        mDefaultAlarmAlertFilename = SystemProperties.get(DEFAULT_RINGTONE_PROPERTY_PREFIX
3823156bb00dd3ab3d6ec732bb6a64a2e84d08c34f0Patrick Scott                + Settings.System.ALARM_ALERT);
3839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
38420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
3859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private MyMediaScannerClient mClient = new MyMediaScannerClient();
38620b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
387e4eb4d9de37a8e9ed6f43a5f7deccf88f28bfc8bMarco Nelissen    private boolean isDrmEnabled() {
3881f89c4f9f8df5d7125221250993ba1e853ed9fedGloria Wang        String prop = SystemProperties.get("drm.service.enabled");
389e4eb4d9de37a8e9ed6f43a5f7deccf88f28bfc8bMarco Nelissen        return prop != null && prop.equals("true");
390e4eb4d9de37a8e9ed6f43a5f7deccf88f28bfc8bMarco Nelissen    }
391e4eb4d9de37a8e9ed6f43a5f7deccf88f28bfc8bMarco Nelissen
3929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private class MyMediaScannerClient implements MediaScannerClient {
39320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
3949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private String mArtist;
3959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private String mAlbumArtist;    // use this if mArtist is missing
3969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private String mAlbum;
3979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private String mTitle;
3989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private String mComposer;
3999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private String mGenre;
4009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private String mMimeType;
4019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private int mFileType;
4029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private int mTrack;
4039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private int mYear;
4049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private int mDuration;
4059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private String mPath;
4069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private long mLastModified;
4079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private long mFileSize;
408c9706f0262a2f399f95e0d78ba2b7616e08d0069Sahil Sachdeva        private String mWriter;
40920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
4109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public FileCacheEntry beginFile(String path, String mimeType, long lastModified, long fileSize) {
41120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
4129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // special case certain file names
41320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync            // I use regionMatches() instead of substring() below
4149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // to avoid memory allocation
4159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int lastSlash = path.lastIndexOf('/');
4169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (lastSlash >= 0 && lastSlash + 2 < path.length()) {
4179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // ignore those ._* files created by MacOS
4189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (path.regionMatches(lastSlash + 1, "._", 0, 2)) {
4199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return null;
4209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
42120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
4229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // ignore album art files created by Windows Media Player:
4239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // Folder.jpg, AlbumArtSmall.jpg, AlbumArt_{...}_Large.jpg and AlbumArt_{...}_Small.jpg
4249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (path.regionMatches(true, path.length() - 4, ".jpg", 0, 4)) {
4259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (path.regionMatches(true, lastSlash + 1, "AlbumArt_{", 0, 10) ||
4269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            path.regionMatches(true, lastSlash + 1, "AlbumArt.", 0, 9)) {
4279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        return null;
4289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
4299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    int length = path.length() - lastSlash - 1;
4309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if ((length == 17 && path.regionMatches(true, lastSlash + 1, "AlbumArtSmall", 0, 13)) ||
4319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            (length == 10 && path.regionMatches(true, lastSlash + 1, "Folder", 0, 6))) {
4329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        return null;
4339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
4349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
4359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
43620b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
4379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mMimeType = null;
438c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood            mFileType = 0;
439c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood            mFileSize = fileSize;
440c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood
4419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // try mimeType first, if it is specified
4429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mimeType != null) {
4439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mFileType = MediaFile.getFileTypeForMimeType(mimeType);
4449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (mFileType != 0) {
4459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    mMimeType = mimeType;
4469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
4479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
4489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // if mimeType was not specified, compute file type based on file extension.
4509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mMimeType == null) {
4519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                MediaFile.MediaFileType mediaFileType = MediaFile.getFileType(path);
4529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (mediaFileType != null) {
4539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    mFileType = mediaFileType.fileType;
4549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    mMimeType = mediaFileType.mimeType;
4559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
4569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
45720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
458e4eb4d9de37a8e9ed6f43a5f7deccf88f28bfc8bMarco Nelissen            if (isDrmEnabled() && MediaFile.isDrmFileType(mFileType)) {
4595d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang                mFileType = getFileTypeFromDrm(path);
4605d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang            }
4615d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang
4629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String key = path;
463250528e5b2362cb7b6749afb331acd8226f67744Mike Lockwood            if (mMediaStoragePath != null && key.startsWith(mMediaStoragePath)) {
464250528e5b2362cb7b6749afb331acd8226f67744Mike Lockwood                // MediaProvider uses external variant of path for _data, so we need to match
465250528e5b2362cb7b6749afb331acd8226f67744Mike Lockwood                // against that path instead.
466250528e5b2362cb7b6749afb331acd8226f67744Mike Lockwood                key = mExternalStoragePath + key.substring(mMediaStoragePath.length());
467250528e5b2362cb7b6749afb331acd8226f67744Mike Lockwood            }
4689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mCaseInsensitivePaths) {
4699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                key = path.toLowerCase();
4709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
4719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            FileCacheEntry entry = mFileCache.get(key);
4729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (entry == null) {
473c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood                Uri tableUri;
474c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood                if (MediaFile.isVideoFileType(mFileType)) {
475c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood                    tableUri = mVideoUri;
476c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood                } else if (MediaFile.isImageFileType(mFileType)) {
477c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood                    tableUri = mImagesUri;
478c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood                } else if (MediaFile.isAudioFileType(mFileType)) {
479c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood                    tableUri = mAudioUri;
480c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood                } else {
481c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood                    tableUri = mFilesUri;
482c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood                }
483d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood                entry = new FileCacheEntry(tableUri, 0, path, 0, 0);
4849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mFileCache.put(key, entry);
4859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
4869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            entry.mSeenInFileSystem = true;
48720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
4889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // add some slack to avoid a rounding error
4899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            long delta = lastModified - entry.mLastModified;
4909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (delta > 1 || delta < -1) {
4919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                entry.mLastModified = lastModified;
4929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                entry.mLastModifiedChanged = true;
4939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
49420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
4959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mProcessPlaylists && MediaFile.isPlayListFileType(mFileType)) {
4969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mPlayLists.add(entry);
4979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // we don't process playlists in the main scan, so return null
4989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return null;
4999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
50020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
5019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // clear all the metadata
5029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mArtist = null;
5039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mAlbumArtist = null;
5049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mAlbum = null;
5059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mTitle = null;
5069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mComposer = null;
5079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mGenre = null;
5089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mTrack = 0;
5099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mYear = 0;
5109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mDuration = 0;
5119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mPath = path;
5129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mLastModified = lastModified;
513c9706f0262a2f399f95e0d78ba2b7616e08d0069Sahil Sachdeva            mWriter = null;
51420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
5159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return entry;
5169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
51720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
5189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void scanFile(String path, long lastModified, long fileSize) {
51900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            // This is the callback funtion from native codes.
52000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            // Log.v(TAG, "scanFile: "+path);
5219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            doScanFile(path, null, lastModified, fileSize, false);
5229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void scanFile(String path, String mimeType, long lastModified, long fileSize) {
5259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            doScanFile(path, mimeType, lastModified, fileSize, false);
5269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
528c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood        public Uri doScanFile(String path, String mimeType, long lastModified,
529c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood                long fileSize, boolean scanAlways) {
5309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Uri result = null;
5319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project//            long t1 = System.currentTimeMillis();
5329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
5339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                FileCacheEntry entry = beginFile(path, mimeType, lastModified, fileSize);
5349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // rescan for metadata if file was modified since last scan
5359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (entry != null && (entry.mLastModifiedChanged || scanAlways)) {
5369f0b1a48506af8e18162f62570561135b65a4ed1Marco Nelissen                    String lowpath = path.toLowerCase();
5379f0b1a48506af8e18162f62570561135b65a4ed1Marco Nelissen                    boolean ringtones = (lowpath.indexOf(RINGTONES_DIR) > 0);
5389f0b1a48506af8e18162f62570561135b65a4ed1Marco Nelissen                    boolean notifications = (lowpath.indexOf(NOTIFICATIONS_DIR) > 0);
5399f0b1a48506af8e18162f62570561135b65a4ed1Marco Nelissen                    boolean alarms = (lowpath.indexOf(ALARMS_DIR) > 0);
5409f0b1a48506af8e18162f62570561135b65a4ed1Marco Nelissen                    boolean podcasts = (lowpath.indexOf(PODCAST_DIR) > 0);
5419f0b1a48506af8e18162f62570561135b65a4ed1Marco Nelissen                    boolean music = (lowpath.indexOf(MUSIC_DIR) > 0) ||
5429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        (!ringtones && !notifications && !alarms && !podcasts);
5439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
544c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood                    // we only extract metadata for audio and video files
545c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood                    if (MediaFile.isAudioFileType(mFileType)
546c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood                            || MediaFile.isVideoFileType(mFileType)) {
5479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        processFile(path, mimeType, this);
5489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
54920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
5509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    result = endFile(entry, ringtones, notifications, alarms, music, podcasts);
5519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
5529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (RemoteException e) {
5539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e);
5549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
5559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project//            long t2 = System.currentTimeMillis();
5569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project//            Log.v(TAG, "scanFile: " + path + " took " + (t2-t1));
5579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return result;
5589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private int parseSubstring(String s, int start, int defaultValue) {
5619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int length = s.length();
5629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (start == length) return defaultValue;
5639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            char ch = s.charAt(start++);
5659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // return defaultValue if we have no integer at all
5669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (ch < '0' || ch > '9') return defaultValue;
56720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
5689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int result = ch - '0';
5699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            while (start < length) {
5709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                ch = s.charAt(start++);
5719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (ch < '0' || ch > '9') return result;
5729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                result = result * 10 + (ch - '0');
5739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
57420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
5759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return result;
57620b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync        }
57720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
5789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void handleStringTag(String name, String value) {
5799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (name.equalsIgnoreCase("title") || name.startsWith("title;")) {
58073bb511adee5444ecd041146fbbd3677fb635949Marco Nelissen                // Don't trim() here, to preserve the special \001 character
58173bb511adee5444ecd041146fbbd3677fb635949Marco Nelissen                // used to force sorting. The media provider will trim() before
58273bb511adee5444ecd041146fbbd3677fb635949Marco Nelissen                // inserting the title in to the database.
58373bb511adee5444ecd041146fbbd3677fb635949Marco Nelissen                mTitle = value;
5849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (name.equalsIgnoreCase("artist") || name.startsWith("artist;")) {
5859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mArtist = value.trim();
586ed8188711a794867b47d21bbed3e26827f6c27e3Neel Parekh            } else if (name.equalsIgnoreCase("albumartist") || name.startsWith("albumartist;")
587ed8188711a794867b47d21bbed3e26827f6c27e3Neel Parekh                    || name.equalsIgnoreCase("band") || name.startsWith("band;")) {
5889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mAlbumArtist = value.trim();
5899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (name.equalsIgnoreCase("album") || name.startsWith("album;")) {
5909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mAlbum = value.trim();
5919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (name.equalsIgnoreCase("composer") || name.startsWith("composer;")) {
5929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mComposer = value.trim();
5939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (name.equalsIgnoreCase("genre") || name.startsWith("genre;")) {
5949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // handle numeric genres, which PV sometimes encodes like "(20)"
5959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (value.length() > 0) {
5969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    int genreCode = -1;
5979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    char ch = value.charAt(0);
5989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (ch == '(') {
5999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        genreCode = parseSubstring(value, 1, -1);
6009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else if (ch >= '0' && ch <= '9') {
6019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        genreCode = parseSubstring(value, 0, -1);
6029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
6039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (genreCode >= 0 && genreCode < ID3_GENRES.length) {
6049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        value = ID3_GENRES[genreCode];
605bfcb22a207499c7ecd4aef9bea2ac778d8be8fc8Marco Nelissen                    } else if (genreCode == 255) {
606bfcb22a207499c7ecd4aef9bea2ac778d8be8fc8Marco Nelissen                        // 255 is defined to be unknown
607bfcb22a207499c7ecd4aef9bea2ac778d8be8fc8Marco Nelissen                        value = null;
6089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
6099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
6109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mGenre = value;
6119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (name.equalsIgnoreCase("year") || name.startsWith("year;")) {
6129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mYear = parseSubstring(value, 0, 0);
6139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (name.equalsIgnoreCase("tracknumber") || name.startsWith("tracknumber;")) {
6149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // track number might be of the form "2/12"
6159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // we just read the number before the slash
6169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int num = parseSubstring(value, 0, 0);
61720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync                mTrack = (mTrack / 1000) * 1000 + num;
6189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (name.equalsIgnoreCase("discnumber") ||
6199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    name.equals("set") || name.startsWith("set;")) {
6209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // set number might be of the form "1/3"
6219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // we just read the number before the slash
6229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int num = parseSubstring(value, 0, 0);
6239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mTrack = (num * 1000) + (mTrack % 1000);
6249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (name.equalsIgnoreCase("duration")) {
6259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mDuration = parseSubstring(value, 0, 0);
626c9706f0262a2f399f95e0d78ba2b7616e08d0069Sahil Sachdeva            } else if (name.equalsIgnoreCase("writer") || name.startsWith("writer;")) {
627c9706f0262a2f399f95e0d78ba2b7616e08d0069Sahil Sachdeva                mWriter = value.trim();
6289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
6299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
63020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
6319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void setMimeType(String mimeType) {
6321851db6804cbdf8d2c8ede0725e73be6fda0f3c0Marco Nelissen            if ("audio/mp4".equals(mMimeType) &&
6331851db6804cbdf8d2c8ede0725e73be6fda0f3c0Marco Nelissen                    mimeType.startsWith("video")) {
6341851db6804cbdf8d2c8ede0725e73be6fda0f3c0Marco Nelissen                // for feature parity with Donut, we force m4a files to keep the
6351851db6804cbdf8d2c8ede0725e73be6fda0f3c0Marco Nelissen                // audio/mp4 mimetype, even if they are really "enhanced podcasts"
6361851db6804cbdf8d2c8ede0725e73be6fda0f3c0Marco Nelissen                // with a video track
6371851db6804cbdf8d2c8ede0725e73be6fda0f3c0Marco Nelissen                return;
6381851db6804cbdf8d2c8ede0725e73be6fda0f3c0Marco Nelissen            }
6399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mMimeType = mimeType;
6409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mFileType = MediaFile.getFileTypeForMimeType(mimeType);
6419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
64220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
6439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
6449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Formats the data into a values array suitable for use with the Media
6459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Content Provider.
64620b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync         *
6479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return a map of values
6489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
6499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private ContentValues toValues() {
6509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ContentValues map = new ContentValues();
6519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            map.put(MediaStore.MediaColumns.DATA, mPath);
6539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            map.put(MediaStore.MediaColumns.TITLE, mTitle);
6549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            map.put(MediaStore.MediaColumns.DATE_MODIFIED, mLastModified);
6559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            map.put(MediaStore.MediaColumns.SIZE, mFileSize);
6569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            map.put(MediaStore.MediaColumns.MIME_TYPE, mMimeType);
65720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
6589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (MediaFile.isVideoFileType(mFileType)) {
6599a488b44cb479bc42375d2cfcdd4aa6161dc17cfMarco Nelissen                map.put(Video.Media.ARTIST, (mArtist != null && mArtist.length() > 0 ? mArtist : MediaStore.UNKNOWN_STRING));
6609a488b44cb479bc42375d2cfcdd4aa6161dc17cfMarco Nelissen                map.put(Video.Media.ALBUM, (mAlbum != null && mAlbum.length() > 0 ? mAlbum : MediaStore.UNKNOWN_STRING));
6619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                map.put(Video.Media.DURATION, mDuration);
6629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // FIXME - add RESOLUTION
6639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (MediaFile.isImageFileType(mFileType)) {
6649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // FIXME - add DESCRIPTION
6659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (MediaFile.isAudioFileType(mFileType)) {
6660096b8d7d119281466763160d5c88c5f9832d005Marco Nelissen                map.put(Audio.Media.ARTIST, (mArtist != null && mArtist.length() > 0) ?
6670096b8d7d119281466763160d5c88c5f9832d005Marco Nelissen                        mArtist : MediaStore.UNKNOWN_STRING);
668920d649949f6636a9e4220904c9a3c5cd860fd53Marco Nelissen                map.put(Audio.Media.ALBUM_ARTIST, (mAlbumArtist != null &&
669920d649949f6636a9e4220904c9a3c5cd860fd53Marco Nelissen                        mAlbumArtist.length() > 0) ? mAlbumArtist : null);
6700096b8d7d119281466763160d5c88c5f9832d005Marco Nelissen                map.put(Audio.Media.ALBUM, (mAlbum != null && mAlbum.length() > 0) ?
6710096b8d7d119281466763160d5c88c5f9832d005Marco Nelissen                        mAlbum : MediaStore.UNKNOWN_STRING);
6729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                map.put(Audio.Media.COMPOSER, mComposer);
6739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (mYear != 0) {
6749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    map.put(Audio.Media.YEAR, mYear);
6759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
6769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                map.put(Audio.Media.TRACK, mTrack);
6779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                map.put(Audio.Media.DURATION, mDuration);
6789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
6799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return map;
6809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
68120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
6829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private Uri endFile(FileCacheEntry entry, boolean ringtones, boolean notifications,
68320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync                boolean alarms, boolean music, boolean podcasts)
6849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                throws RemoteException {
6859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // update database
68620b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
6879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project             // use album artist if artist is missing
6889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mArtist == null || mArtist.length() == 0) {
6899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mArtist = mAlbumArtist;
6909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
6919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ContentValues values = toValues();
6939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String title = values.getAsString(MediaStore.MediaColumns.TITLE);
694bdc0e82f9c075edb67da4a2583e557824c280415Mike Lockwood            if (title == null || TextUtils.isEmpty(title.trim())) {
695c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood                title = MediaFile.getFileTitle(values.getAsString(MediaStore.MediaColumns.DATA));
6969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                values.put(MediaStore.MediaColumns.TITLE, title);
6979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
698c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen            String album = values.getAsString(Audio.Media.ALBUM);
6999a488b44cb479bc42375d2cfcdd4aa6161dc17cfMarco Nelissen            if (MediaStore.UNKNOWN_STRING.equals(album)) {
700c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                album = values.getAsString(MediaStore.MediaColumns.DATA);
701c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                // extract last path segment before file name
702c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                int lastSlash = album.lastIndexOf('/');
703c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                if (lastSlash >= 0) {
704c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                    int previousSlash = 0;
705c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                    while (true) {
706c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                        int idx = album.indexOf('/', previousSlash + 1);
707c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                        if (idx < 0 || idx >= lastSlash) {
708c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                            break;
709c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                        }
710c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                        previousSlash = idx;
711c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                    }
712c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                    if (previousSlash != 0) {
713c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                        album = album.substring(previousSlash + 1, lastSlash);
714c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                        values.put(Audio.Media.ALBUM, album);
715c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                    }
716c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                }
717c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen            }
718c066557378c2305625a7bad3d25f084ad647ef27Marco Nelissen            long rowId = entry.mRowId;
719c2206a0710a3ca2c29854c1a0638c5ba55629ba2Mike Lockwood            if (MediaFile.isAudioFileType(mFileType) && (rowId == 0 || mMtpObjectHandle != 0)) {
720c066557378c2305625a7bad3d25f084ad647ef27Marco Nelissen                // Only set these for new entries. For existing entries, they
721c066557378c2305625a7bad3d25f084ad647ef27Marco Nelissen                // may have been modified later, and we want to keep the current
722c066557378c2305625a7bad3d25f084ad647ef27Marco Nelissen                // values so that custom ringtones still show up in the ringtone
723c066557378c2305625a7bad3d25f084ad647ef27Marco Nelissen                // picker.
7249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                values.put(Audio.Media.IS_RINGTONE, ringtones);
7259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                values.put(Audio.Media.IS_NOTIFICATION, notifications);
7269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                values.put(Audio.Media.IS_ALARM, alarms);
7279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                values.put(Audio.Media.IS_MUSIC, music);
7289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                values.put(Audio.Media.IS_PODCAST, podcasts);
72920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync            } else if (mFileType == MediaFile.FILE_TYPE_JPEG) {
730700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang                ExifInterface exif = null;
731700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang                try {
732700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang                    exif = new ExifInterface(entry.mPath);
733700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang                } catch (IOException ex) {
734700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang                    // exif is null
735700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang                }
736700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang                if (exif != null) {
737872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang                    float[] latlng = new float[2];
738872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang                    if (exif.getLatLong(latlng)) {
73920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync                        values.put(Images.Media.LATITUDE, latlng[0]);
74020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync                        values.put(Images.Media.LONGITUDE, latlng[1]);
74120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync                    }
742099397cbd07c8c991f3126d0d0ac64bb6b3c0b47Chih-Chung Chang
7433a8cab88e7747dc280ce85895af014f98e80a6ccRay Chen                    long time = exif.getGpsDateTime();
744099397cbd07c8c991f3126d0d0ac64bb6b3c0b47Chih-Chung Chang                    if (time != -1) {
745052d9c8b414dc9eb1c70d849660592d9bc3b2644Ray Chen                        values.put(Images.Media.DATE_TAKEN, time);
746099397cbd07c8c991f3126d0d0ac64bb6b3c0b47Chih-Chung Chang                    }
747b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang
748b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                    int orientation = exif.getAttributeInt(
749b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                        ExifInterface.TAG_ORIENTATION, -1);
750b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                    if (orientation != -1) {
751b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                        // We only recognize a subset of orientation tag values.
752b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                        int degree;
753b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                        switch(orientation) {
754b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                            case ExifInterface.ORIENTATION_ROTATE_90:
755b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                                degree = 90;
756b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                                break;
757b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                            case ExifInterface.ORIENTATION_ROTATE_180:
758b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                                degree = 180;
759b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                                break;
760b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                            case ExifInterface.ORIENTATION_ROTATE_270:
761b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                                degree = 270;
762b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                                break;
763b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                            default:
764b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                                degree = 0;
765b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                                break;
766b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                        }
767b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                        values.put(Images.Media.ORIENTATION, degree);
768b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                    }
76920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync                }
7709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
77120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
772c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood            Uri tableUri = entry.mTableUri;
7739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Uri result = null;
7749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (rowId == 0) {
775c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood                if (mMtpObjectHandle != 0) {
776c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood                    values.put(MediaStore.MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID, mMtpObjectHandle);
777c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood                }
778c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood                if (tableUri == mFilesUri) {
779c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood                    values.put(Files.FileColumns.FORMAT, MediaFile.getFormatCode(entry.mPath, mMimeType));
780c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood                }
7819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // new file, insert it
7829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                result = mMediaProvider.insert(tableUri, values);
7839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (result != null) {
7849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    rowId = ContentUris.parseId(result);
7859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    entry.mRowId = rowId;
7869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
7879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
7889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // updated file
7899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                result = ContentUris.withAppendedId(tableUri, rowId);
7909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mMediaProvider.update(result, values, null, null);
7919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
7929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mProcessGenres && mGenre != null) {
7939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                String genre = mGenre;
7949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Uri uri = mGenreCache.get(genre);
7959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (uri == null) {
7969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    Cursor cursor = null;
7979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    try {
7989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        // see if the genre already exists
7999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        cursor = mMediaProvider.query(
8009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                mGenresUri,
8019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                GENRE_LOOKUP_PROJECTION, MediaStore.Audio.Genres.NAME + "=?",
8029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                        new String[] { genre }, null);
8039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (cursor == null || cursor.getCount() == 0) {
8049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            // genre does not exist, so create the genre in the genre table
8059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            values.clear();
8069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            values.put(MediaStore.Audio.Genres.NAME, genre);
8079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            uri = mMediaProvider.insert(mGenresUri, values);
8089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        } else {
8099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            // genre already exists, so compute its Uri
8109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            cursor.moveToNext();
8119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            uri = ContentUris.withAppendedId(mGenresUri, cursor.getLong(0));
8129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
8139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (uri != null) {
8149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            uri = Uri.withAppendedPath(uri, Genres.Members.CONTENT_DIRECTORY);
8159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            mGenreCache.put(genre, uri);
8169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
8179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } finally {
8189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        // release the cursor if it exists
8199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (cursor != null) {
8209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            cursor.close();
8219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
8229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
8239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
82420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
8259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (uri != null) {
82620b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync                    // add entry to audio_genre_map
8279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    values.clear();
8289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    values.put(MediaStore.Audio.Genres.Members.AUDIO_ID, Long.valueOf(rowId));
8299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    mMediaProvider.insert(uri, values);
8309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
8319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
83220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
8339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (notifications && !mDefaultNotificationSet) {
8349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (TextUtils.isEmpty(mDefaultNotificationFilename) ||
8359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        doesPathHaveFilename(entry.mPath, mDefaultNotificationFilename)) {
8369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    setSettingIfNotSet(Settings.System.NOTIFICATION_SOUND, tableUri, rowId);
8379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    mDefaultNotificationSet = true;
8389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
8399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (ringtones && !mDefaultRingtoneSet) {
8409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (TextUtils.isEmpty(mDefaultRingtoneFilename) ||
8419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        doesPathHaveFilename(entry.mPath, mDefaultRingtoneFilename)) {
8429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    setSettingIfNotSet(Settings.System.RINGTONE, tableUri, rowId);
8439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    mDefaultRingtoneSet = true;
8449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
8453156bb00dd3ab3d6ec732bb6a64a2e84d08c34f0Patrick Scott            } else if (alarms && !mDefaultAlarmSet) {
8463156bb00dd3ab3d6ec732bb6a64a2e84d08c34f0Patrick Scott                if (TextUtils.isEmpty(mDefaultAlarmAlertFilename) ||
8473156bb00dd3ab3d6ec732bb6a64a2e84d08c34f0Patrick Scott                        doesPathHaveFilename(entry.mPath, mDefaultAlarmAlertFilename)) {
8483156bb00dd3ab3d6ec732bb6a64a2e84d08c34f0Patrick Scott                    setSettingIfNotSet(Settings.System.ALARM_ALERT, tableUri, rowId);
8493156bb00dd3ab3d6ec732bb6a64a2e84d08c34f0Patrick Scott                    mDefaultAlarmSet = true;
8503156bb00dd3ab3d6ec732bb6a64a2e84d08c34f0Patrick Scott                }
8519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
85220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
8539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return result;
8549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
85520b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
8569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private boolean doesPathHaveFilename(String path, String filename) {
8579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int pathFilenameStart = path.lastIndexOf(File.separatorChar) + 1;
8589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int filenameLength = filename.length();
8599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return path.regionMatches(pathFilenameStart, filename, 0, filenameLength) &&
8609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    pathFilenameStart + filenameLength == path.length();
8619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
86220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
8639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private void setSettingIfNotSet(String settingName, Uri uri, long rowId) {
86420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
8659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String existingSettingValue = Settings.System.getString(mContext.getContentResolver(),
8669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    settingName);
86720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
8689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (TextUtils.isEmpty(existingSettingValue)) {
8699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // Set the setting to the given URI
8709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Settings.System.putString(mContext.getContentResolver(), settingName,
8719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        ContentUris.withAppendedId(uri, rowId).toString());
8729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
8739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
87420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
8758b0466170082fdf12f78d7b70358002520e99e9bMarco Nelissen        public void addNoMediaFolder(String path) {
8768b0466170082fdf12f78d7b70358002520e99e9bMarco Nelissen            ContentValues values = new ContentValues();
8778b0466170082fdf12f78d7b70358002520e99e9bMarco Nelissen            values.put(MediaStore.Images.ImageColumns.DATA, "");
8788b0466170082fdf12f78d7b70358002520e99e9bMarco Nelissen            String [] pathSpec = new String[] {path + '%'};
8798b0466170082fdf12f78d7b70358002520e99e9bMarco Nelissen            try {
88033f49c8377ea9ffebaca9c398fb3c0f9585f9fe8Marco Nelissen                // These tables have DELETE_FILE triggers that delete the file from the
88133f49c8377ea9ffebaca9c398fb3c0f9585f9fe8Marco Nelissen                // sd card when deleting the database entry. We don't want to do this in
88233f49c8377ea9ffebaca9c398fb3c0f9585f9fe8Marco Nelissen                // this case, since it would cause those files to be removed if a .nomedia
88333f49c8377ea9ffebaca9c398fb3c0f9585f9fe8Marco Nelissen                // file was added after the fact, when in that case we only want the database
88433f49c8377ea9ffebaca9c398fb3c0f9585f9fe8Marco Nelissen                // entries to be removed.
8858b0466170082fdf12f78d7b70358002520e99e9bMarco Nelissen                mMediaProvider.update(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values,
8868b0466170082fdf12f78d7b70358002520e99e9bMarco Nelissen                        MediaStore.Images.ImageColumns.DATA + " LIKE ?", pathSpec);
8878b0466170082fdf12f78d7b70358002520e99e9bMarco Nelissen                mMediaProvider.update(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, values,
8888b0466170082fdf12f78d7b70358002520e99e9bMarco Nelissen                        MediaStore.Images.ImageColumns.DATA + " LIKE ?", pathSpec);
8898b0466170082fdf12f78d7b70358002520e99e9bMarco Nelissen            } catch (RemoteException e) {
8908b0466170082fdf12f78d7b70358002520e99e9bMarco Nelissen                throw new RuntimeException();
8918b0466170082fdf12f78d7b70358002520e99e9bMarco Nelissen            }
8928b0466170082fdf12f78d7b70358002520e99e9bMarco Nelissen        }
8938b0466170082fdf12f78d7b70358002520e99e9bMarco Nelissen
8945d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang        private int getFileTypeFromDrm(String path) {
895e4eb4d9de37a8e9ed6f43a5f7deccf88f28bfc8bMarco Nelissen            if (!isDrmEnabled()) {
8965d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang                return 0;
8975d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang            }
8985d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang
8995d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang            int resultFileType = 0;
9005d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang
9015d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang            if (mDrmManagerClient == null) {
9025d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang                mDrmManagerClient = new DrmManagerClient(mContext);
9035d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang            }
9045d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang
9055d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang            if (mDrmManagerClient.canHandle(path, null)) {
9065d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang                String drmMimetype = mDrmManagerClient.getOriginalMimeType(path);
9075d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang                if (drmMimetype != null) {
9085d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang                    mMimeType = drmMimetype;
9095d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang                    resultFileType = MediaFile.getFileTypeForMimeType(drmMimetype);
9105d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang                }
9115d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang            }
9125d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang            return resultFileType;
9135d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang        }
9145d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang
9159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }; // end of anonymous MediaScannerClient instance
91620b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
917c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood    private void prescan(String filePath, boolean prescanFiles) throws RemoteException {
9189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Cursor c = null;
9199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String where = null;
9209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String[] selectionArgs = null;
92120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
9229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mFileCache == null) {
9239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mFileCache = new HashMap<String, FileCacheEntry>();
9249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
9259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mFileCache.clear();
9269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mPlayLists == null) {
9289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mPlayLists = new ArrayList<FileCacheEntry>();
9299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
9309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mPlayLists.clear();
9319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
93220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
933c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood        if (filePath != null) {
934250528e5b2362cb7b6749afb331acd8226f67744Mike Lockwood            if (mMediaStoragePath != null && filePath.startsWith(mMediaStoragePath)) {
935250528e5b2362cb7b6749afb331acd8226f67744Mike Lockwood                // MediaProvider uses external variant of path for _data, so we need to query
936250528e5b2362cb7b6749afb331acd8226f67744Mike Lockwood                // using that path instead.
937250528e5b2362cb7b6749afb331acd8226f67744Mike Lockwood                filePath = mExternalStoragePath + filePath.substring(mMediaStoragePath.length());
938250528e5b2362cb7b6749afb331acd8226f67744Mike Lockwood            }
939250528e5b2362cb7b6749afb331acd8226f67744Mike Lockwood
940c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood            // query for only one file
941c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood            where = Files.FileColumns.DATA + "=?";
942c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood            selectionArgs = new String[] { filePath };
943c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood        }
944c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood
9459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Build the list of files from the content provider
9469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
947c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood            if (prescanFiles) {
948c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood                // First read existing files from the files table
94920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
950d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood                c = mMediaProvider.query(mFilesUri, FILES_PRESCAN_PROJECTION,
951d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood                        where, selectionArgs, null);
952c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood
953c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood                if (c != null) {
9549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    while (c.moveToNext()) {
955d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood                        long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX);
956d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood                        String path = c.getString(FILES_PRESCAN_PATH_COLUMN_INDEX);
957d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood                        int format = c.getInt(FILES_PRESCAN_FORMAT_COLUMN_INDEX);
958d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood                        long lastModified = c.getLong(FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
95920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
96035b002a9c873fb40116179c8626b9841b0abae74Marco Nelissen                        // Only consider entries with absolute path names.
96135b002a9c873fb40116179c8626b9841b0abae74Marco Nelissen                        // This allows storing URIs in the database without the
96235b002a9c873fb40116179c8626b9841b0abae74Marco Nelissen                        // media scanner removing them.
96335b002a9c873fb40116179c8626b9841b0abae74Marco Nelissen                        if (path.startsWith("/")) {
96435b002a9c873fb40116179c8626b9841b0abae74Marco Nelissen                            String key = path;
96535b002a9c873fb40116179c8626b9841b0abae74Marco Nelissen                            if (mCaseInsensitivePaths) {
96635b002a9c873fb40116179c8626b9841b0abae74Marco Nelissen                                key = path.toLowerCase();
96735b002a9c873fb40116179c8626b9841b0abae74Marco Nelissen                            }
968c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood
969c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood                            FileCacheEntry entry = new FileCacheEntry(mFilesUri, rowId, path,
970d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood                                    lastModified, format);
971c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood                            mFileCache.put(key, entry);
9729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
9739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
9749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    c.close();
9759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    c = null;
9769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
9779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        finally {
9809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (c != null) {
9819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                c.close();
9829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9846458935331ccfacb4cccc50d6b893947febae687Mike Lockwood
9856458935331ccfacb4cccc50d6b893947febae687Mike Lockwood        // compute original size of images
9866458935331ccfacb4cccc50d6b893947febae687Mike Lockwood        mOriginalCount = 0;
9876458935331ccfacb4cccc50d6b893947febae687Mike Lockwood        c = mMediaProvider.query(mImagesUri, ID_PROJECTION, null, null, null);
9886458935331ccfacb4cccc50d6b893947febae687Mike Lockwood        if (c != null) {
9896458935331ccfacb4cccc50d6b893947febae687Mike Lockwood            mOriginalCount = c.getCount();
9906458935331ccfacb4cccc50d6b893947febae687Mike Lockwood            c.close();
9916458935331ccfacb4cccc50d6b893947febae687Mike Lockwood        }
9929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
99320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
9949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean inScanDirectory(String path, String[] directories) {
9959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < directories.length; i++) {
9969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (path.startsWith(directories[i])) {
9979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return true;
9989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return false;
10019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
100220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
10039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void pruneDeadThumbnailFiles() {
10049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        HashSet<String> existingFiles = new HashSet<String>();
10059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String directory = "/sdcard/DCIM/.thumbnails";
10069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String [] files = (new File(directory)).list();
10079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (files == null)
10089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            files = new String[0];
100920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
10109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < files.length; i++) {
10119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String fullPathString = directory + "/" + files[i];
10129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            existingFiles.add(fullPathString);
10139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
101420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
10159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
10169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Cursor c = mMediaProvider.query(
101720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync                    mThumbsUri,
101820b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync                    new String [] { "_data" },
101920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync                    null,
102020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync                    null,
10219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    null);
10229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.v(TAG, "pruneDeadThumbnailFiles... " + c);
10239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (c != null && c.moveToFirst()) {
10249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                do {
10259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    String fullPathString = c.getString(0);
10269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    existingFiles.remove(fullPathString);
10279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } while (c.moveToNext());
10289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
102920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
10309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (String fileToDelete : existingFiles) {
10319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (Config.LOGV)
10329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    Log.v(TAG, "fileToDelete is " + fileToDelete);
10339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                try {
10349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    (new File(fileToDelete)).delete();
10359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } catch (SecurityException ex) {
10369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
10379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
103820b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
10399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.v(TAG, "/pruneDeadThumbnailFiles... " + c);
10409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (c != null) {
10419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                c.close();
10429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
10439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (RemoteException e) {
10449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // We will soon be killed...
10459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
10479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void postscan(String[] directories) throws RemoteException {
10499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Iterator<FileCacheEntry> iterator = mFileCache.values().iterator();
10509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        while (iterator.hasNext()) {
10529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            FileCacheEntry entry = iterator.next();
10539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String path = entry.mPath;
105420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
10559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // remove database entries for files that no longer exist.
10569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            boolean fileMissing = false;
105720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
1058d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood            if (!entry.mSeenInFileSystem && !MtpConstants.isAbstractObject(entry.mFormat)) {
1059d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood                if (entry.mFormat != MtpConstants.FORMAT_ASSOCIATION &&
1060d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood                        inScanDirectory(path, directories)) {
10619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // we didn't see this file in the scan directory.
10629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    fileMissing = true;
10639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
1064d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood                    // the file actually a directory or other abstract object
1065d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood                    // or is outside of our scan directory,
10669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // so we need to check for file existence here.
10679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    File testFile = new File(path);
10689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (!testFile.exists()) {
10699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        fileMissing = true;
10709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
10719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
10729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
107320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
10749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (fileMissing) {
10759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // do not delete missing playlists, since they may have been modified by the user.
10769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // the user can delete them in the media player instead.
10779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // instead, clear the path and lastModified fields in the row
10789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                MediaFile.MediaFileType mediaFileType = MediaFile.getFileType(path);
10799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int fileType = (mediaFileType == null ? 0 : mediaFileType.fileType);
10809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (MediaFile.isPlayListFileType(fileType)) {
10829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    ContentValues values = new ContentValues();
10839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    values.put(MediaStore.Audio.Playlists.DATA, "");
10849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    values.put(MediaStore.Audio.Playlists.DATE_MODIFIED, 0);
1085d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood                    mMediaProvider.update(ContentUris.withAppendedId(mPlaylistsUri, entry.mRowId),
1086d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood                            values, null, null);
10879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
1088d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood                    mMediaProvider.delete(ContentUris.withAppendedId(mFilesUri, entry.mRowId),
1089d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood                            null, null);
10909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    iterator.remove();
10919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
10929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
10939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
109420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
10959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // handle playlists last, after we know what media files are on the storage.
10969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mProcessPlaylists) {
10979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            processPlayLists();
10989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
109920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
11009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mOriginalCount == 0 && mImagesUri.equals(Images.Media.getContentUri("external")))
11019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            pruneDeadThumbnailFiles();
110220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
11039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // allow GC to clean up
11049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mGenreCache = null;
11059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mPlayLists = null;
11069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mFileCache = null;
11079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mMediaProvider = null;
11089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
110920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
11109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void initialize(String volumeName) {
11119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mMediaProvider = mContext.getContentResolver().acquireProvider("media");
111220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
11139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mAudioUri = Audio.Media.getContentUri(volumeName);
11149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mVideoUri = Video.Media.getContentUri(volumeName);
11159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mImagesUri = Images.Media.getContentUri(volumeName);
11169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mThumbsUri = Images.Thumbnails.getContentUri(volumeName);
1117c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood        mFilesUri = Files.getContentUri(volumeName);
11189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!volumeName.equals("internal")) {
11209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // we only support playlists on external media
11219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mProcessPlaylists = true;
11229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mProcessGenres = true;
11239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mGenreCache = new HashMap<String, Uri>();
11249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mGenresUri = Genres.getContentUri(volumeName);
11259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mPlaylistsUri = Playlists.getContentUri(volumeName);
1126f73738b78a8396552274cf33b0021f414fb7201dMike Lockwood
1127f73738b78a8396552274cf33b0021f414fb7201dMike Lockwood            mCaseInsensitivePaths = !mContext.getResources().getBoolean(
1128f73738b78a8396552274cf33b0021f414fb7201dMike Lockwood                com.android.internal.R.bool.config_caseSensitiveExternalStorage);
1129f73738b78a8396552274cf33b0021f414fb7201dMike Lockwood            if (!Process.supportsProcesses()) {
1130f73738b78a8396552274cf33b0021f414fb7201dMike Lockwood                // Simulator uses host file system, so it should be case sensitive.
1131f73738b78a8396552274cf33b0021f414fb7201dMike Lockwood                mCaseInsensitivePaths = false;
11329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
113320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync        }
11349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void scanDirectories(String[] directories, String volumeName) {
11379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
11389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            long start = System.currentTimeMillis();
113920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync            initialize(volumeName);
1140c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood            prescan(null, true);
11419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            long prescan = System.currentTimeMillis();
114220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
11439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < directories.length; i++) {
1144c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood                processDirectory(directories[i], mClient);
11459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
11469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            long scan = System.currentTimeMillis();
11479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            postscan(directories);
11489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            long end = System.currentTimeMillis();
114920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
11509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (Config.LOGD) {
11519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Log.d(TAG, " prescan time: " + (prescan - start) + "ms\n");
11529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Log.d(TAG, "    scan time: " + (scan - prescan) + "ms\n");
11539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Log.d(TAG, "postscan time: " + (end - scan) + "ms\n");
11549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Log.d(TAG, "   total time: " + (end - start) + "ms\n");
11559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
11569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (SQLException e) {
11579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // this might happen if the SD card is removed while the media scanner is running
11589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.e(TAG, "SQLException in MediaScanner.scan()", e);
11599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (UnsupportedOperationException e) {
11609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // this might happen if the SD card is removed while the media scanner is running
11619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.e(TAG, "UnsupportedOperationException in MediaScanner.scan()", e);
11629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (RemoteException e) {
11639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.e(TAG, "RemoteException in MediaScanner.scan()", e);
11649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
11659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // this function is used to scan a single file
11689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public Uri scanSingleFile(String path, String volumeName, String mimeType) {
11699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
117020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync            initialize(volumeName);
1171c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood            prescan(path, true);
117220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
11739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            File file = new File(path);
1174ec74abb71f79cdc573c536115acc7ff26a02680aMikael
1175ec74abb71f79cdc573c536115acc7ff26a02680aMikael            // lastModified is in milliseconds on Files.
1176ec74abb71f79cdc573c536115acc7ff26a02680aMikael            long lastModifiedSeconds = file.lastModified() / 1000;
1177ec74abb71f79cdc573c536115acc7ff26a02680aMikael
11789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // always scan the file, so we can return the content://media Uri for existing files
1179ec74abb71f79cdc573c536115acc7ff26a02680aMikael            return mClient.doScanFile(path, mimeType, lastModifiedSeconds, file.length(), true);
11809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (RemoteException e) {
11819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e);
11829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return null;
11839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
11849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1186c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood    public void scanMtpFile(String path, String volumeName, int objectHandle, int format) {
1187c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood        MediaFile.MediaFileType mediaFileType = MediaFile.getFileType(path);
1188c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood        int fileType = (mediaFileType == null ? 0 : mediaFileType.fileType);
1189c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood
1190c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood        if (!MediaFile.isAudioFileType(fileType) && !MediaFile.isVideoFileType(fileType) &&
1191c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood            !MediaFile.isImageFileType(fileType) && !MediaFile.isPlayListFileType(fileType)) {
1192c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood            // nothing to do
1193c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood            return;
1194c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood        }
1195c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood
1196d815f79766984fce499e147ecbacc01914683f74Mike Lockwood        mMtpObjectHandle = objectHandle;
1197c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood        try {
1198c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood            initialize(volumeName);
1199c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood            // MTP will create a file entry for us so we don't want to do it in prescan
1200c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood            prescan(path, false);
1201c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood
1202c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood            File file = new File(path);
1203c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood
1204c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood            // lastModified is in milliseconds on Files.
1205c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood            long lastModifiedSeconds = file.lastModified() / 1000;
1206c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood
1207c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood            // always scan the file, so we can return the content://media Uri for existing files
1208c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood            mClient.doScanFile(path, mediaFileType.mimeType, lastModifiedSeconds, file.length(), true);
1209c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood        } catch (RemoteException e) {
1210c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood            Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e);
1211c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood        } finally {
1212c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood            mMtpObjectHandle = 0;
1213c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood        }
1214d815f79766984fce499e147ecbacc01914683f74Mike Lockwood    }
1215d815f79766984fce499e147ecbacc01914683f74Mike Lockwood
12169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // returns the number of matching file/directory names, starting from the right
12179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int matchPaths(String path1, String path2) {
12189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int result = 0;
12199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int end1 = path1.length();
12209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int end2 = path2.length();
122120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
12229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        while (end1 > 0 && end2 > 0) {
12239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int slash1 = path1.lastIndexOf('/', end1 - 1);
12249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int slash2 = path2.lastIndexOf('/', end2 - 1);
12259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int backSlash1 = path1.lastIndexOf('\\', end1 - 1);
12269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int backSlash2 = path2.lastIndexOf('\\', end2 - 1);
12279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int start1 = (slash1 > backSlash1 ? slash1 : backSlash1);
12289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int start2 = (slash2 > backSlash2 ? slash2 : backSlash2);
12299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (start1 < 0) start1 = 0; else start1++;
12309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (start2 < 0) start2 = 0; else start2++;
12319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int length = end1 - start1;
12329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (end2 - start2 != length) break;
12339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (path1.regionMatches(true, start1, path2, start2, length)) {
12349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                result++;
12359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                end1 = start1 - 1;
12369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                end2 = start2 - 1;
12379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else break;
12389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
123920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
12409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return result;
12419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
12429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
124320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync    private boolean addPlayListEntry(String entry, String playListDirectory,
12449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Uri uri, ContentValues values, int index) {
124520b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
12469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // watch for trailing whitespace
12479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int entryLength = entry.length();
12489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        while (entryLength > 0 && Character.isWhitespace(entry.charAt(entryLength - 1))) entryLength--;
12499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // path should be longer than 3 characters.
12509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // avoid index out of bounds errors below by returning here.
12519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (entryLength < 3) return false;
12529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (entryLength < entry.length()) entry = entry.substring(0, entryLength);
12539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // does entry appear to be an absolute path?
12559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // look for Unix or DOS absolute paths
12569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        char ch1 = entry.charAt(0);
12579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean fullPath = (ch1 == '/' ||
12589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                (Character.isLetter(ch1) && entry.charAt(1) == ':' && entry.charAt(2) == '\\'));
12599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // if we have a relative path, combine entry with playListDirectory
12609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!fullPath)
12619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            entry = playListDirectory + entry;
126220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
12639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        //FIXME - should we look for "../" within the path?
126420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
12659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // best matching MediaFile for the play list entry
12669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        FileCacheEntry bestMatch = null;
126720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
12689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // number of rightmost file/directory names for bestMatch
126920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync        int bestMatchLength = 0;
127020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
12719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Iterator<FileCacheEntry> iterator = mFileCache.values().iterator();
12729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        while (iterator.hasNext()) {
12739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            FileCacheEntry cacheEntry = iterator.next();
12749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String path = cacheEntry.mPath;
127520b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
12769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (path.equalsIgnoreCase(entry)) {
12779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                bestMatch = cacheEntry;
12789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;    // don't bother continuing search
12799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
128020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
12819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int matchLength = matchPaths(path, entry);
12829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (matchLength > bestMatchLength) {
12839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                bestMatch = cacheEntry;
12849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                bestMatchLength = matchLength;
12859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
12869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
128720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
1288d4f3586c93cd9c29056e95cd79f3cb19ac456f3bMarco Nelissen        // if the match is not for an audio file, bail out
1289d4f3586c93cd9c29056e95cd79f3cb19ac456f3bMarco Nelissen        if (bestMatch == null || ! mAudioUri.equals(bestMatch.mTableUri)) {
12909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return false;
12919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
129220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
12939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
12949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // OK, now we need to add this to the database
12959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            values.clear();
12969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            values.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER, Integer.valueOf(index));
12979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            values.put(MediaStore.Audio.Playlists.Members.AUDIO_ID, Long.valueOf(bestMatch.mRowId));
12989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mMediaProvider.insert(uri, values);
12999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (RemoteException e) {
13009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.e(TAG, "RemoteException in MediaScanner.addPlayListEntry()", e);
13019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return false;
13029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return true;
13059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
130620b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
13079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void processM3uPlayList(String path, String playListDirectory, Uri uri, ContentValues values) {
13089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        BufferedReader reader = null;
13099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
13109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            File f = new File(path);
13119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (f.exists()) {
13129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                reader = new BufferedReader(
13139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        new InputStreamReader(new FileInputStream(f)), 8192);
13149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                String line = reader.readLine();
13159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int index = 0;
13169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                while (line != null) {
13179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // ignore comment lines, which begin with '#'
13189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (line.length() > 0 && line.charAt(0) != '#') {
13199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        values.clear();
13209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (addPlayListEntry(line, playListDirectory, uri, values, index))
13219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            index++;
13229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
13239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    line = reader.readLine();
13249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
13259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
13269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IOException e) {
13279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.e(TAG, "IOException in MediaScanner.processM3uPlayList()", e);
13289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } finally {
13299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
13309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (reader != null)
13319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    reader.close();
13329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (IOException e) {
13339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Log.e(TAG, "IOException in MediaScanner.processM3uPlayList()", e);
13349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
13359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
13379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void processPlsPlayList(String path, String playListDirectory, Uri uri, ContentValues values) {
13399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        BufferedReader reader = null;
13409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
13419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            File f = new File(path);
13429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (f.exists()) {
13439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                reader = new BufferedReader(
13449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        new InputStreamReader(new FileInputStream(f)), 8192);
13459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                String line = reader.readLine();
13469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int index = 0;
13479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                while (line != null) {
13489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // ignore comment lines, which begin with '#'
13499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (line.startsWith("File")) {
13509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        int equals = line.indexOf('=');
13519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (equals > 0) {
13529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            values.clear();
13539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            if (addPlayListEntry(line.substring(equals + 1), playListDirectory, uri, values, index))
13549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                index++;
13559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
13569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
13579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    line = reader.readLine();
13589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
13599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
13609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IOException e) {
13619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.e(TAG, "IOException in MediaScanner.processPlsPlayList()", e);
13629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } finally {
13639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
13649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (reader != null)
13659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    reader.close();
13669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (IOException e) {
13679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Log.e(TAG, "IOException in MediaScanner.processPlsPlayList()", e);
13689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
13699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
13719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    class WplHandler implements ElementListener {
13739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final ContentHandler handler;
13759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String playListDirectory;
13769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Uri uri;
13779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        ContentValues values = new ContentValues();
13789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int index = 0;
13799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public WplHandler(String playListDirectory, Uri uri) {
13819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.playListDirectory = playListDirectory;
13829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.uri = uri;
138320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
13849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            RootElement root = new RootElement("smil");
13859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Element body = root.getChild("body");
13869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Element seq = body.getChild("seq");
13879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Element media = seq.getChild("media");
13889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            media.setElementListener(this);
13899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.handler = root.getContentHandler();
13919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void start(Attributes attributes) {
13949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String path = attributes.getValue("", "src");
13959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (path != null) {
13969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                values.clear();
13979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (addPlayListEntry(path, playListDirectory, uri, values, index)) {
13989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    index++;
13999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
14009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
14019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project       public void end() {
14049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project       }
14059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        ContentHandler getContentHandler() {
14079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return handler;
14089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
14109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void processWplPlayList(String path, String playListDirectory, Uri uri) {
14129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        FileInputStream fis = null;
14139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
14149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            File f = new File(path);
14159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (f.exists()) {
14169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                fis = new FileInputStream(f);
14179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Xml.parse(fis, Xml.findEncodingByName("UTF-8"), new WplHandler(playListDirectory, uri).getContentHandler());
14199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
14209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (SAXException e) {
14219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            e.printStackTrace();
14229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IOException e) {
14239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            e.printStackTrace();
14249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } finally {
14259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
14269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (fis != null)
14279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    fis.close();
14289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (IOException e) {
14299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Log.e(TAG, "IOException in MediaScanner.processWplPlayList()", e);
14309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
14319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
143320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
14349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void processPlayLists() throws RemoteException {
14359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Iterator<FileCacheEntry> iterator = mPlayLists.iterator();
14369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        while (iterator.hasNext()) {
14379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            FileCacheEntry entry = iterator.next();
143820b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync            String path = entry.mPath;
14399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // only process playlist files if they are new or have been modified since the last scan
14419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (entry.mLastModifiedChanged) {
14429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                ContentValues values = new ContentValues();
14439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int lastSlash = path.lastIndexOf('/');
14449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (lastSlash < 0) throw new IllegalArgumentException("bad path " + path);
14459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Uri uri, membersUri;
14469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                long rowId = entry.mRowId;
14479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (rowId == 0) {
14489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // Create a new playlist
144920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
14509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    int lastDot = path.lastIndexOf('.');
14519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    String name = (lastDot < 0 ? path.substring(lastSlash + 1) : path.substring(lastSlash + 1, lastDot));
14529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    values.put(MediaStore.Audio.Playlists.NAME, name);
14539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    values.put(MediaStore.Audio.Playlists.DATA, path);
14549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    values.put(MediaStore.Audio.Playlists.DATE_MODIFIED, entry.mLastModified);
14559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    uri = mMediaProvider.insert(mPlaylistsUri, values);
14569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    rowId = ContentUris.parseId(uri);
14579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    membersUri = Uri.withAppendedPath(uri, Playlists.Members.CONTENT_DIRECTORY);
14589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
14599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    uri = ContentUris.withAppendedId(mPlaylistsUri, rowId);
146020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
14619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // update lastModified value of existing playlist
14629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    values.put(MediaStore.Audio.Playlists.DATE_MODIFIED, entry.mLastModified);
14639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    mMediaProvider.update(uri, values, null, null);
14649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // delete members of existing playlist
14669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    membersUri = Uri.withAppendedPath(uri, Playlists.Members.CONTENT_DIRECTORY);
14679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    mMediaProvider.delete(membersUri, null, null);
14689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
146920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
14709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                String playListDirectory = path.substring(0, lastSlash + 1);
14719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                MediaFile.MediaFileType mediaFileType = MediaFile.getFileType(path);
14729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int fileType = (mediaFileType == null ? 0 : mediaFileType.fileType);
14739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (fileType == MediaFile.FILE_TYPE_M3U)
14759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    processM3uPlayList(path, playListDirectory, membersUri, values);
14769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                else if (fileType == MediaFile.FILE_TYPE_PLS)
14779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    processPlsPlayList(path, playListDirectory, membersUri, values);
14789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                else if (fileType == MediaFile.FILE_TYPE_WPL)
14799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    processWplPlayList(path, playListDirectory, membersUri);
148020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
14819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Cursor cursor = mMediaProvider.query(membersUri, PLAYLIST_MEMBERS_PROJECTION, null,
14829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        null, null);
14839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                try {
14849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (cursor == null || cursor.getCount() == 0) {
14859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        Log.d(TAG, "playlist is empty - deleting");
14869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        mMediaProvider.delete(uri, null, null);
14879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
14889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } finally {
14899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (cursor != null) cursor.close();
14909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
14919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
14929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
149420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
1495c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood    private native void processDirectory(String path, MediaScannerClient client);
14969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private native void processFile(String path, String mimeType, MediaScannerClient client);
14979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public native void setLocale(String locale);
149820b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
14999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public native byte[] extractAlbumArt(FileDescriptor fd);
15009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15014935d05eaa306cef88cf0ab13eca386f270409ecMarco Nelissen    private static native final void native_init();
15029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private native final void native_setup();
15039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private native final void native_finalize();
15049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
150520b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync    protected void finalize() {
15069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mContext.getContentResolver().releaseProvider(mMediaProvider);
150720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync        native_finalize();
15089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
15099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
1510