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
1960cfad80bdf61db436643927337c2fb30186e99dJeff Sharkeyimport android.content.ContentProviderClient;
2068528911b9f74475ac79c369a6482f720259dbacMarco Nelissenimport android.content.ContentResolver;
21bfcb22a207499c7ecd4aef9bea2ac778d8be8fc8Marco Nelissenimport android.content.ContentUris;
229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.ContentValues;
239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.Context;
2417d24400906235a172fc760e98656589692f798aJean-Michel Triviimport android.content.SharedPreferences;
259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.database.Cursor;
269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.database.SQLException;
275d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wangimport android.drm.DrmManagerClient;
289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.BitmapFactory;
290cd0136d440cf6ad9d5fab430269116786e671ecMike Lockwoodimport android.mtp.MtpConstants;
309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.net.Uri;
3117d24400906235a172fc760e98656589692f798aJean-Michel Triviimport android.os.Build;
32250528e5b2362cb7b6749afb331acd8226f67744Mike Lockwoodimport android.os.Environment;
339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.RemoteException;
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.SystemProperties;
359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.provider.MediaStore;
369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.provider.MediaStore.Audio;
370b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissenimport android.provider.MediaStore.Audio.Playlists;
38c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwoodimport android.provider.MediaStore.Files;
390b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissenimport android.provider.MediaStore.Files.FileColumns;
409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.provider.MediaStore.Images;
419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.provider.MediaStore.Video;
420b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissenimport android.provider.Settings;
4368528911b9f74475ac79c369a6482f720259dbacMarco Nelissenimport android.provider.Settings.SettingNotFoundException;
449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.sax.Element;
459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.sax.ElementListener;
469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.sax.RootElement;
4734385d352da19805ae948215e2edbeedd16b7941Elliott Hughesimport android.system.ErrnoException;
4834385d352da19805ae948215e2edbeedd16b7941Elliott Hughesimport android.system.Os;
499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.TextUtils;
509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.Log;
519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.Xml;
529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5360cfad80bdf61db436643927337c2fb30186e99dJeff Sharkeyimport dalvik.system.CloseGuard;
5460cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey
5560cfad80bdf61db436643927337c2fb30186e99dJeff Sharkeyimport org.xml.sax.Attributes;
5660cfad80bdf61db436643927337c2fb30186e99dJeff Sharkeyimport org.xml.sax.ContentHandler;
5760cfad80bdf61db436643927337c2fb30186e99dJeff Sharkeyimport org.xml.sax.SAXException;
5860cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey
59bfcb22a207499c7ecd4aef9bea2ac778d8be8fc8Marco Nelissenimport java.io.BufferedReader;
60bfcb22a207499c7ecd4aef9bea2ac778d8be8fc8Marco Nelissenimport java.io.File;
61bfcb22a207499c7ecd4aef9bea2ac778d8be8fc8Marco Nelissenimport java.io.FileDescriptor;
62bfcb22a207499c7ecd4aef9bea2ac778d8be8fc8Marco Nelissenimport java.io.FileInputStream;
63bfcb22a207499c7ecd4aef9bea2ac778d8be8fc8Marco Nelissenimport java.io.IOException;
64bfcb22a207499c7ecd4aef9bea2ac778d8be8fc8Marco Nelissenimport java.io.InputStreamReader;
655f0c3a531938c0c1399efab94906aee74eff9108Bryan Mawhinneyimport java.text.SimpleDateFormat;
665f0c3a531938c0c1399efab94906aee74eff9108Bryan Mawhinneyimport java.text.ParseException;
679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.ArrayList;
6893a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissenimport java.util.HashMap;
699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.HashSet;
709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.Iterator;
71ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissenimport java.util.Locale;
725f0c3a531938c0c1399efab94906aee74eff9108Bryan Mawhinneyimport java.util.TimeZone;
7360cfad80bdf61db436643927337c2fb30186e99dJeff Sharkeyimport java.util.concurrent.atomic.AtomicBoolean;
749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
7677f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * Internal service helper that no-one should use directly.
7720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync *
7877f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * The way the scan currently works is:
7977f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * - The Java MediaScannerService creates a MediaScanner (this class), and calls
8077f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen *   MediaScanner.scanDirectories on it.
8177f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * - scanDirectories() calls the native processDirectory() for each of the specified directories.
8277f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * - the processDirectory() JNI method wraps the provided mediascanner client in a native
8377f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen *   'MyMediaScannerClient' class, then calls processDirectory() on the native MediaScanner
8477f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen *   object (which got created when the Java MediaScanner was created).
857ae82ce1fd54103bb87b8a0593a4228c94b1d6f9James Dong * - native MediaScanner.processDirectory() calls
8677f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen *   doProcessDirectory(), which recurses over the folder, and calls
8777f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen *   native MyMediaScannerClient.scanFile() for every file whose extension matches.
8877f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * - native MyMediaScannerClient.scanFile() calls back on Java MediaScannerClient.scanFile,
8977f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen *   which calls doScanFile, which after some setup calls back down to native code, calling
9077f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen *   MediaScanner.processFile().
9177f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * - MediaScanner.processFile() calls one of several methods, depending on the type of the
9277f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen *   file: parseMP3, parseMP4, parseMidi, parseOgg or parseWMA.
9377f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * - each of these methods gets metadata key/value pairs from the file, and repeatedly
9477f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen *   calls native MyMediaScannerClient.handleStringTag, which calls back up to its Java
9577f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen *   counterparts in this file.
9677f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * - Java handleStringTag() gathers the key/value pairs that it's interested in.
9777f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * - once processFile returns and we're back in Java code in doScanFile(), it calls
9877f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen *   Java MyMediaScannerClient.endFile(), which takes all the data that's been
9977f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen *   gathered and inserts an entry in to the database.
10077f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen *
10177f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * In summary:
10277f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * Java MediaScannerService calls
10377f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * Java MediaScanner scanDirectories, which calls
10477f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * Java MediaScanner processDirectory (native method), which calls
10577f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * native MediaScanner processDirectory, which calls
10677f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * native MyMediaScannerClient scanFile, which calls
10777f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * Java MyMediaScannerClient scanFile, which calls
10877f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * Java MediaScannerClient doScanFile, which calls
10977f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * Java MediaScanner processFile (native method), which calls
11077f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * native MediaScanner processFile, which calls
11177f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * native parseMP3, parseMP4, parseMidi, parseOgg or parseWMA, which calls
11277f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * native MyMediaScanner handleStringTag, which calls
11377f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * Java MyMediaScanner handleStringTag.
11477f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * Once MediaScanner processFile returns, an entry is inserted in to the database.
1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
116af16d0b30e89f31b7617424c8706553ff571d08dMike Lockwood * The MediaScanner class is not thread-safe, so it should only be used in a single threaded manner.
117af16d0b30e89f31b7617424c8706553ff571d08dMike Lockwood *
1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * {@hide}
1199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
12060cfad80bdf61db436643927337c2fb30186e99dJeff Sharkeypublic class MediaScanner implements AutoCloseable {
1219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static {
1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        System.loadLibrary("media_jni");
1234935d05eaa306cef88cf0ab13eca386f270409ecMarco Nelissen        native_init();
1249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private final static String TAG = "MediaScanner";
1279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
128d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood    private static final String[] FILES_PRESCAN_PROJECTION = new String[] {
129c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood            Files.FileColumns._ID, // 0
130c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood            Files.FileColumns.DATA, // 1
131d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood            Files.FileColumns.FORMAT, // 2
132d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood            Files.FileColumns.DATE_MODIFIED, // 3
1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    };
13420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
1356458935331ccfacb4cccc50d6b893947febae687Mike Lockwood    private static final String[] ID_PROJECTION = new String[] {
1366458935331ccfacb4cccc50d6b893947febae687Mike Lockwood            Files.FileColumns._ID,
1376458935331ccfacb4cccc50d6b893947febae687Mike Lockwood    };
1386458935331ccfacb4cccc50d6b893947febae687Mike Lockwood
139d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood    private static final int FILES_PRESCAN_ID_COLUMN_INDEX = 0;
140d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood    private static final int FILES_PRESCAN_PATH_COLUMN_INDEX = 1;
141d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood    private static final int FILES_PRESCAN_FORMAT_COLUMN_INDEX = 2;
142d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood    private static final int FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX = 3;
143d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood
1449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String[] PLAYLIST_MEMBERS_PROJECTION = new String[] {
1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Audio.Playlists.Members.PLAYLIST_ID, // 0
1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     };
1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int ID_PLAYLISTS_COLUMN_INDEX = 0;
1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int PATH_PLAYLISTS_COLUMN_INDEX = 1;
1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int DATE_MODIFIED_PLAYLISTS_COLUMN_INDEX = 2;
1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String RINGTONES_DIR = "/ringtones/";
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String NOTIFICATIONS_DIR = "/notifications/";
1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String ALARMS_DIR = "/alarms/";
1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String MUSIC_DIR = "/music/";
1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String PODCAST_DIR = "/podcasts/";
15720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
15817d24400906235a172fc760e98656589692f798aJean-Michel Trivi    public static final String SCANNED_BUILD_PREFS_NAME = "MediaScanBuild";
15917d24400906235a172fc760e98656589692f798aJean-Michel Trivi    public static final String LAST_INTERNAL_SCAN_FINGERPRINT = "lastScanFingerprint";
16017d24400906235a172fc760e98656589692f798aJean-Michel Trivi    private static final String SYSTEM_SOUNDS_DIR = "/system/media/audio";
161c31033f43c85a07ebc6045c9b5af5408d0b306e9Jaekyun Seok    private static final String PRODUCT_SOUNDS_DIR = "/product/media/audio";
16217d24400906235a172fc760e98656589692f798aJean-Michel Trivi    private static String sLastInternalScanFingerprint;
16317d24400906235a172fc760e98656589692f798aJean-Michel Trivi
1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String[] ID3_GENRES = {
1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // ID3v1 Genres
1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Blues",
1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Classic Rock",
1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Country",
1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Dance",
1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Disco",
1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Funk",
1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Grunge",
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Hip-Hop",
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Jazz",
1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Metal",
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "New Age",
1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Oldies",
1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Other",
1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Pop",
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "R&B",
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Rap",
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Reggae",
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Rock",
1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Techno",
1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Industrial",
1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Alternative",
1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Ska",
1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Death Metal",
1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Pranks",
1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Soundtrack",
1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Euro-Techno",
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Ambient",
1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Trip-Hop",
1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Vocal",
1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Jazz+Funk",
1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Fusion",
1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Trance",
1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Classical",
1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Instrumental",
2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Acid",
2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "House",
2029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Game",
2039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Sound Clip",
2049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Gospel",
2059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Noise",
2069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "AlternRock",
2079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Bass",
2089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Soul",
2099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Punk",
2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Space",
2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Meditative",
2129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Instrumental Pop",
2139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Instrumental Rock",
2149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Ethnic",
2159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Gothic",
2169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Darkwave",
2179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Techno-Industrial",
2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Electronic",
2199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Pop-Folk",
2209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Eurodance",
2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Dream",
2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Southern Rock",
2239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Comedy",
2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Cult",
2259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Gangsta",
2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Top 40",
2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Christian Rap",
2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Pop/Funk",
2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Jungle",
2309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Native American",
2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Cabaret",
2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "New Wave",
2339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Psychadelic",
2349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Rave",
2359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Showtunes",
2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Trailer",
2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Lo-Fi",
2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Tribal",
2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Acid Punk",
2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Acid Jazz",
2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Polka",
2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Retro",
2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Musical",
2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Rock & Roll",
2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Hard Rock",
2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // The following genres are Winamp extensions
2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Folk",
2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Folk-Rock",
2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "National Folk",
2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Swing",
2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Fast Fusion",
2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Bebob",
2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Latin",
2549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Revival",
2559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Celtic",
2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Bluegrass",
2579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Avantgarde",
2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Gothic Rock",
2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Progressive Rock",
2609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Psychedelic Rock",
2619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Symphonic Rock",
2629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Slow Rock",
2639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Big Band",
2649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Chorus",
2659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Easy Listening",
2669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Acoustic",
2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Humour",
2689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Speech",
2699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Chanson",
2709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Opera",
2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Chamber Music",
2729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Sonata",
2739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Symphony",
2749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Booty Bass",
2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Primus",
2769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Porn Groove",
2779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Satire",
2789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Slow Jam",
2799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Club",
2809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Tango",
2819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Samba",
2829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Folklore",
2839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Ballad",
2849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Power Ballad",
2859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Rhythmic Soul",
2869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Freestyle",
2879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Duet",
2889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Punk Rock",
2899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Drum Solo",
2909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "A capella",
2919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Euro-House",
292cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        "Dance Hall",
293cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        // The following ones seem to be fairly widely supported as well
294cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        "Goa",
295cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        "Drum & Bass",
296cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        "Club-House",
297cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        "Hardcore",
298cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        "Terror",
299cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        "Indie",
300cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        "Britpop",
301c3ea477e2111f199d10341b121791cb2ec91eca3Johan Redestig        null,
302cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        "Polsk Punk",
303cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        "Beat",
304cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        "Christian Gangsta",
305cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        "Heavy Metal",
306cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        "Black Metal",
307cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        "Crossover",
308cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        "Contemporary Christian",
309cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        "Christian Rock",
310cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        "Merengue",
311cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        "Salsa",
312cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        "Thrash Metal",
313cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        "Anime",
314cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        "JPop",
315cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        "Synthpop",
316cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        // 148 and up don't seem to have been defined yet.
3179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    };
3189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
319075e9a19ce645752f8282bc19c91b25978a7dc52Ashok Bhat    private long mNativeContext;
32060cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey    private final Context mContext;
32160cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey    private final String mPackageName;
32260cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey    private final String mVolumeName;
32360cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey    private final ContentProviderClient mMediaProvider;
32460cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey    private final Uri mAudioUri;
32560cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey    private final Uri mVideoUri;
32660cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey    private final Uri mImagesUri;
32760cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey    private final Uri mPlaylistsUri;
32860cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey    private final Uri mFilesUri;
32960cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey    private final Uri mFilesUriNoNotify;
33060cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey    private final boolean mProcessPlaylists;
33160cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey    private final boolean mProcessGenres;
332d815f79766984fce499e147ecbacc01914683f74Mike Lockwood    private int mMtpObjectHandle;
3339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
33460cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey    private final AtomicBoolean mClosed = new AtomicBoolean();
33560cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey    private final CloseGuard mCloseGuard = CloseGuard.get();
336250528e5b2362cb7b6749afb331acd8226f67744Mike Lockwood
337d6b3e0cc36cbe28c8d194b63227df37c76af7efaMarco Nelissen    /** whether to use bulk inserts or individual inserts for each item */
338d6b3e0cc36cbe28c8d194b63227df37c76af7efaMarco Nelissen    private static final boolean ENABLE_BULK_INSERTS = true;
3393410b4656505673c72da558dcfec94d7b135e760Mike Lockwood
3409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // used when scanning the image database so we know whether we have to prune
3419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // old thumbnail files
3429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mOriginalCount;
3439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** Whether the scanner has set a default sound for the ringer ringtone. */
3449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean mDefaultRingtoneSet;
3459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** Whether the scanner has set a default sound for the notification ringtone. */
3469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean mDefaultNotificationSet;
3473156bb00dd3ab3d6ec732bb6a64a2e84d08c34f0Patrick Scott    /** Whether the scanner has set a default sound for the alarm ringtone. */
3483156bb00dd3ab3d6ec732bb6a64a2e84d08c34f0Patrick Scott    private boolean mDefaultAlarmSet;
3499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** The filename for the default sound for the ringer ringtone. */
3509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private String mDefaultRingtoneFilename;
3519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** The filename for the default sound for the notification ringtone. */
3529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private String mDefaultNotificationFilename;
3533156bb00dd3ab3d6ec732bb6a64a2e84d08c34f0Patrick Scott    /** The filename for the default sound for the alarm ringtone. */
3543156bb00dd3ab3d6ec732bb6a64a2e84d08c34f0Patrick Scott    private String mDefaultAlarmAlertFilename;
3559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * The prefix for system properties that define the default sound for
3579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * ringtones. Concatenate the name of the setting from Settings
3589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * to get the full system property.
3599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String DEFAULT_RINGTONE_PROPERTY_PREFIX = "ro.config.";
36120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
36230c918ce7fbe171944b28fc91b3f22b3d631872dGlenn Kasten    private final BitmapFactory.Options mBitmapOptions = new BitmapFactory.Options();
3639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
36458ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen    private static class FileEntry {
3659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        long mRowId;
3669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String mPath;
3679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        long mLastModified;
368d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood        int mFormat;
3699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean mLastModifiedChanged;
37020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
37158ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen        FileEntry(long rowId, String path, long lastModified, int format) {
3729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mRowId = rowId;
3739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mPath = path;
3749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mLastModified = lastModified;
375d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood            mFormat = format;
3769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mLastModifiedChanged = false;
3779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        @Override
3809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String toString() {
3813e5e8055942ee8179675b8ed11d40ca12dbe42e6Marco Nelissen            return mPath + " mRowId: " + mRowId;
3829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
38420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
3850b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen    private static class PlaylistEntry {
3860b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen        String path;
3870b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen        long bestmatchid;
3880b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen        int bestmatchlevel;
3890b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen    }
3900b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen
39160cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey    private final ArrayList<PlaylistEntry> mPlaylistEntries = new ArrayList<>();
39260cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey    private final ArrayList<FileEntry> mPlayLists = new ArrayList<>();
3930b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen
3948e2ed8d512ade2ea481b3a292556782b17a74674Dongwon Kang    private MediaInserter mMediaInserter;
395bd682b040833fce9e212c00c395b32bec7050f87Mike Lockwood
3965d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang    private DrmManagerClient mDrmManagerClient = null;
3979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
39860cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey    public MediaScanner(Context c, String volumeName) {
3999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        native_setup();
4009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mContext = c;
40135654b61e8fe7bc85afcb076ddbb590d51c5865fDianne Hackborn        mPackageName = c.getPackageName();
40260cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey        mVolumeName = volumeName;
40360cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey
4049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mBitmapOptions.inSampleSize = 1;
4059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mBitmapOptions.inJustDecodeBounds = true;
40620b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
4079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        setDefaultRingtoneFileNames();
408250528e5b2362cb7b6749afb331acd8226f67744Mike Lockwood
40960cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey        mMediaProvider = mContext.getContentResolver()
41060cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey                .acquireContentProviderClient(MediaStore.AUTHORITY);
41160cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey
41217d24400906235a172fc760e98656589692f798aJean-Michel Trivi        if (sLastInternalScanFingerprint == null) {
41317d24400906235a172fc760e98656589692f798aJean-Michel Trivi            final SharedPreferences scanSettings =
41417d24400906235a172fc760e98656589692f798aJean-Michel Trivi                    mContext.getSharedPreferences(SCANNED_BUILD_PREFS_NAME, Context.MODE_PRIVATE);
41517d24400906235a172fc760e98656589692f798aJean-Michel Trivi            sLastInternalScanFingerprint =
41617d24400906235a172fc760e98656589692f798aJean-Michel Trivi                    scanSettings.getString(LAST_INTERNAL_SCAN_FINGERPRINT, new String());
41717d24400906235a172fc760e98656589692f798aJean-Michel Trivi        }
41817d24400906235a172fc760e98656589692f798aJean-Michel Trivi
41960cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey        mAudioUri = Audio.Media.getContentUri(volumeName);
42060cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey        mVideoUri = Video.Media.getContentUri(volumeName);
42160cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey        mImagesUri = Images.Media.getContentUri(volumeName);
42260cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey        mFilesUri = Files.getContentUri(volumeName);
42360cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey        mFilesUriNoNotify = mFilesUri.buildUpon().appendQueryParameter("nonotify", "1").build();
42460cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey
42560cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey        if (!volumeName.equals("internal")) {
42660cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey            // we only support playlists on external media
42760cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey            mProcessPlaylists = true;
42860cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey            mProcessGenres = true;
42960cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey            mPlaylistsUri = Playlists.getContentUri(volumeName);
43060cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey        } else {
43160cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey            mProcessPlaylists = false;
43260cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey            mProcessGenres = false;
43360cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey            mPlaylistsUri = null;
43460cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey        }
43560cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey
43660cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey        final Locale locale = mContext.getResources().getConfiguration().locale;
43760cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey        if (locale != null) {
43860cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey            String language = locale.getLanguage();
43960cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey            String country = locale.getCountry();
44060cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey            if (language != null) {
44160cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey                if (country != null) {
44260cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey                    setLocale(language + "_" + country);
44360cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey                } else {
44460cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey                    setLocale(language);
44560cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey                }
44660cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey            }
44760cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey        }
44860cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey
44960cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey        mCloseGuard.open("close");
4509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void setDefaultRingtoneFileNames() {
4539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mDefaultRingtoneFilename = SystemProperties.get(DEFAULT_RINGTONE_PROPERTY_PREFIX
4549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                + Settings.System.RINGTONE);
4559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mDefaultNotificationFilename = SystemProperties.get(DEFAULT_RINGTONE_PROPERTY_PREFIX
4569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                + Settings.System.NOTIFICATION_SOUND);
4573156bb00dd3ab3d6ec732bb6a64a2e84d08c34f0Patrick Scott        mDefaultAlarmAlertFilename = SystemProperties.get(DEFAULT_RINGTONE_PROPERTY_PREFIX
4583156bb00dd3ab3d6ec732bb6a64a2e84d08c34f0Patrick Scott                + Settings.System.ALARM_ALERT);
4599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
46020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
46130c918ce7fbe171944b28fc91b3f22b3d631872dGlenn Kasten    private final MyMediaScannerClient mClient = new MyMediaScannerClient();
46220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
463e4eb4d9de37a8e9ed6f43a5f7deccf88f28bfc8bMarco Nelissen    private boolean isDrmEnabled() {
4641f89c4f9f8df5d7125221250993ba1e853ed9fedGloria Wang        String prop = SystemProperties.get("drm.service.enabled");
465e4eb4d9de37a8e9ed6f43a5f7deccf88f28bfc8bMarco Nelissen        return prop != null && prop.equals("true");
466e4eb4d9de37a8e9ed6f43a5f7deccf88f28bfc8bMarco Nelissen    }
467fbc4939d2b51d511858846363bf02c7c9f851ed2Mike Lockwood
4689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private class MyMediaScannerClient implements MediaScannerClient {
46920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
4705f0c3a531938c0c1399efab94906aee74eff9108Bryan Mawhinney        private final SimpleDateFormat mDateFormatter;
4715f0c3a531938c0c1399efab94906aee74eff9108Bryan Mawhinney
4729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private String mArtist;
4739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private String mAlbumArtist;    // use this if mArtist is missing
4749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private String mAlbum;
4759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private String mTitle;
4769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private String mComposer;
4779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private String mGenre;
4789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private String mMimeType;
4799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private int mFileType;
4809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private int mTrack;
4819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private int mYear;
4829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private int mDuration;
4839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private String mPath;
4845f0c3a531938c0c1399efab94906aee74eff9108Bryan Mawhinney        private long mDate;
4859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private long mLastModified;
4869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private long mFileSize;
487c9706f0262a2f399f95e0d78ba2b7616e08d0069Sahil Sachdeva        private String mWriter;
488f679ba7d944a403fbdc77926c44978ffd5161f2dMarco Nelissen        private int mCompilation;
48982428a862f325238cfb5646bbd65de3f1a11e7ccGloria Wang        private boolean mIsDrm;
490997354e4b4a9666cedd62282471e97822affced9Mike Lockwood        private boolean mNoMedia;   // flag to suppress file from appearing in media tables
491bab75909fac9c65b71012f4c06302503c2f72be1Marco Nelissen        private boolean mScanSuccess;
492e1bf8efaff42dc33b7a4663f3c9d50d12de81bcbChih-Chung Chang        private int mWidth;
493e1bf8efaff42dc33b7a4663f3c9d50d12de81bcbChih-Chung Chang        private int mHeight;
49420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
4955f0c3a531938c0c1399efab94906aee74eff9108Bryan Mawhinney        public MyMediaScannerClient() {
4965f0c3a531938c0c1399efab94906aee74eff9108Bryan Mawhinney            mDateFormatter = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
4975f0c3a531938c0c1399efab94906aee74eff9108Bryan Mawhinney            mDateFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
4985f0c3a531938c0c1399efab94906aee74eff9108Bryan Mawhinney        }
4995f0c3a531938c0c1399efab94906aee74eff9108Bryan Mawhinney
50058ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen        public FileEntry beginFile(String path, String mimeType, long lastModified,
501997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                long fileSize, boolean isDirectory, boolean noMedia) {
502076e05b488e40fdd946f0d35137fe66a576efe09Mike Lockwood            mMimeType = mimeType;
503076e05b488e40fdd946f0d35137fe66a576efe09Mike Lockwood            mFileType = 0;
504076e05b488e40fdd946f0d35137fe66a576efe09Mike Lockwood            mFileSize = fileSize;
50559c14048bfc0a210077192410afa28ed6f85daa0Erik Almqvist            mIsDrm = false;
506bab75909fac9c65b71012f4c06302503c2f72be1Marco Nelissen            mScanSuccess = true;
50720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
508076e05b488e40fdd946f0d35137fe66a576efe09Mike Lockwood            if (!isDirectory) {
509ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood                if (!noMedia && isNoMediaFile(path)) {
510ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood                    noMedia = true;
5119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
512997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                mNoMedia = noMedia;
51320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
514076e05b488e40fdd946f0d35137fe66a576efe09Mike Lockwood                // try mimeType first, if it is specified
515076e05b488e40fdd946f0d35137fe66a576efe09Mike Lockwood                if (mimeType != null) {
516076e05b488e40fdd946f0d35137fe66a576efe09Mike Lockwood                    mFileType = MediaFile.getFileTypeForMimeType(mimeType);
517076e05b488e40fdd946f0d35137fe66a576efe09Mike Lockwood                }
5189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
519076e05b488e40fdd946f0d35137fe66a576efe09Mike Lockwood                // if mimeType was not specified, compute file type based on file extension.
520076e05b488e40fdd946f0d35137fe66a576efe09Mike Lockwood                if (mFileType == 0) {
521076e05b488e40fdd946f0d35137fe66a576efe09Mike Lockwood                    MediaFile.MediaFileType mediaFileType = MediaFile.getFileType(path);
522076e05b488e40fdd946f0d35137fe66a576efe09Mike Lockwood                    if (mediaFileType != null) {
523076e05b488e40fdd946f0d35137fe66a576efe09Mike Lockwood                        mFileType = mediaFileType.fileType;
524076e05b488e40fdd946f0d35137fe66a576efe09Mike Lockwood                        if (mMimeType == null) {
525076e05b488e40fdd946f0d35137fe66a576efe09Mike Lockwood                            mMimeType = mediaFileType.mimeType;
526076e05b488e40fdd946f0d35137fe66a576efe09Mike Lockwood                        }
527858caeaaa1db4076565e6df06b2b2721ec453c4eMike Lockwood                    }
5289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
52920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
530076e05b488e40fdd946f0d35137fe66a576efe09Mike Lockwood                if (isDrmEnabled() && MediaFile.isDrmFileType(mFileType)) {
531076e05b488e40fdd946f0d35137fe66a576efe09Mike Lockwood                    mFileType = getFileTypeFromDrm(path);
532076e05b488e40fdd946f0d35137fe66a576efe09Mike Lockwood                }
5335d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang            }
5345d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang
53558ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen            FileEntry entry = makeEntryFor(path);
536a8a656b0c595fa876a387a91f8813f88a2d3abe7Marco Nelissen            // add some slack to avoid a rounding error
537a8a656b0c595fa876a387a91f8813f88a2d3abe7Marco Nelissen            long delta = (entry != null) ? (lastModified - entry.mLastModified) : 0;
538a8a656b0c595fa876a387a91f8813f88a2d3abe7Marco Nelissen            boolean wasModified = delta > 1 || delta < -1;
539a8a656b0c595fa876a387a91f8813f88a2d3abe7Marco Nelissen            if (entry == null || wasModified) {
540a8a656b0c595fa876a387a91f8813f88a2d3abe7Marco Nelissen                if (wasModified) {
541a8a656b0c595fa876a387a91f8813f88a2d3abe7Marco Nelissen                    entry.mLastModified = lastModified;
542a8a656b0c595fa876a387a91f8813f88a2d3abe7Marco Nelissen                } else {
54358ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                    entry = new FileEntry(0, path, lastModified,
544a8a656b0c595fa876a387a91f8813f88a2d3abe7Marco Nelissen                            (isDirectory ? MtpConstants.FORMAT_ASSOCIATION : 0));
545a8a656b0c595fa876a387a91f8813f88a2d3abe7Marco Nelissen                }
5469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                entry.mLastModifiedChanged = true;
5479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
54820b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
5499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mProcessPlaylists && MediaFile.isPlayListFileType(mFileType)) {
5509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mPlayLists.add(entry);
5519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // we don't process playlists in the main scan, so return null
5529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return null;
5539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
55420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
5559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // clear all the metadata
5569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mArtist = null;
5579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mAlbumArtist = null;
5589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mAlbum = null;
5599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mTitle = null;
5609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mComposer = null;
5619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mGenre = null;
5629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mTrack = 0;
5639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mYear = 0;
5649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mDuration = 0;
5659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mPath = path;
5665f0c3a531938c0c1399efab94906aee74eff9108Bryan Mawhinney            mDate = 0;
5679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mLastModified = lastModified;
568c9706f0262a2f399f95e0d78ba2b7616e08d0069Sahil Sachdeva            mWriter = null;
569f679ba7d944a403fbdc77926c44978ffd5161f2dMarco Nelissen            mCompilation = 0;
570e1bf8efaff42dc33b7a4663f3c9d50d12de81bcbChih-Chung Chang            mWidth = 0;
571e1bf8efaff42dc33b7a4663f3c9d50d12de81bcbChih-Chung Chang            mHeight = 0;
57220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
5739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return entry;
5749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
57520b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
576bcbddd9342ee5ef48617ce7dcca6c505df3fcf08Marco Nelissen        @Override
577997354e4b4a9666cedd62282471e97822affced9Mike Lockwood        public void scanFile(String path, long lastModified, long fileSize,
578997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                boolean isDirectory, boolean noMedia) {
57900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            // This is the callback funtion from native codes.
58000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            // Log.v(TAG, "scanFile: "+path);
581997354e4b4a9666cedd62282471e97822affced9Mike Lockwood            doScanFile(path, null, lastModified, fileSize, isDirectory, false, noMedia);
5829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
584c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood        public Uri doScanFile(String path, String mimeType, long lastModified,
585997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                long fileSize, boolean isDirectory, boolean scanAlways, boolean noMedia) {
5869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Uri result = null;
5879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project//            long t1 = System.currentTimeMillis();
5889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
58958ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                FileEntry entry = beginFile(path, mimeType, lastModified,
590997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                        fileSize, isDirectory, noMedia);
59181e85bbf671f228600fba059e30cb22565bcfe9dMarco Nelissen
59268528911b9f74475ac79c369a6482f720259dbacMarco Nelissen                if (entry == null) {
59368528911b9f74475ac79c369a6482f720259dbacMarco Nelissen                    return null;
59468528911b9f74475ac79c369a6482f720259dbacMarco Nelissen                }
59568528911b9f74475ac79c369a6482f720259dbacMarco Nelissen
59681e85bbf671f228600fba059e30cb22565bcfe9dMarco Nelissen                // if this file was just inserted via mtp, set the rowid to zero
59781e85bbf671f228600fba059e30cb22565bcfe9dMarco Nelissen                // (even though it already exists in the database), to trigger
59881e85bbf671f228600fba059e30cb22565bcfe9dMarco Nelissen                // the correct code path for updating its entry
59981e85bbf671f228600fba059e30cb22565bcfe9dMarco Nelissen                if (mMtpObjectHandle != 0) {
60081e85bbf671f228600fba059e30cb22565bcfe9dMarco Nelissen                    entry.mRowId = 0;
60181e85bbf671f228600fba059e30cb22565bcfe9dMarco Nelissen                }
60268528911b9f74475ac79c369a6482f720259dbacMarco Nelissen
60317d24400906235a172fc760e98656589692f798aJean-Michel Trivi                if (entry.mPath != null) {
60417d24400906235a172fc760e98656589692f798aJean-Michel Trivi                    if (((!mDefaultNotificationSet &&
60568528911b9f74475ac79c369a6482f720259dbacMarco Nelissen                                doesPathHaveFilename(entry.mPath, mDefaultNotificationFilename))
60668528911b9f74475ac79c369a6482f720259dbacMarco Nelissen                        || (!mDefaultRingtoneSet &&
60768528911b9f74475ac79c369a6482f720259dbacMarco Nelissen                                doesPathHaveFilename(entry.mPath, mDefaultRingtoneFilename))
60868528911b9f74475ac79c369a6482f720259dbacMarco Nelissen                        || (!mDefaultAlarmSet &&
60968528911b9f74475ac79c369a6482f720259dbacMarco Nelissen                                doesPathHaveFilename(entry.mPath, mDefaultAlarmAlertFilename)))) {
61017d24400906235a172fc760e98656589692f798aJean-Michel Trivi                        Log.w(TAG, "forcing rescan of " + entry.mPath +
61117d24400906235a172fc760e98656589692f798aJean-Michel Trivi                                "since ringtone setting didn't finish");
61217d24400906235a172fc760e98656589692f798aJean-Michel Trivi                        scanAlways = true;
61317d24400906235a172fc760e98656589692f798aJean-Michel Trivi                    } else if (isSystemSoundWithMetadata(entry.mPath)
61417d24400906235a172fc760e98656589692f798aJean-Michel Trivi                            && !Build.FINGERPRINT.equals(sLastInternalScanFingerprint)) {
61517d24400906235a172fc760e98656589692f798aJean-Michel Trivi                        // file is located on the system partition where the date cannot be trusted:
61617d24400906235a172fc760e98656589692f798aJean-Michel Trivi                        // rescan if the build fingerprint has changed since the last scan.
61717d24400906235a172fc760e98656589692f798aJean-Michel Trivi                        Log.i(TAG, "forcing rescan of " + entry.mPath
61817d24400906235a172fc760e98656589692f798aJean-Michel Trivi                                + " since build fingerprint changed");
61917d24400906235a172fc760e98656589692f798aJean-Michel Trivi                        scanAlways = true;
62017d24400906235a172fc760e98656589692f798aJean-Michel Trivi                    }
62168528911b9f74475ac79c369a6482f720259dbacMarco Nelissen                }
62268528911b9f74475ac79c369a6482f720259dbacMarco Nelissen
6239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // rescan for metadata if file was modified since last scan
6249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (entry != null && (entry.mLastModifiedChanged || scanAlways)) {
625997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                    if (noMedia) {
626997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                        result = endFile(entry, false, false, false, false, false);
627997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                    } else {
628c861411d4bae0912054ff54c3efee90ae2db6db9Marco Nelissen                        boolean isaudio = MediaFile.isAudioFileType(mFileType);
629c861411d4bae0912054ff54c3efee90ae2db6db9Marco Nelissen                        boolean isvideo = MediaFile.isVideoFileType(mFileType);
630c861411d4bae0912054ff54c3efee90ae2db6db9Marco Nelissen                        boolean isimage = MediaFile.isImageFileType(mFileType);
631c861411d4bae0912054ff54c3efee90ae2db6db9Marco Nelissen
632c861411d4bae0912054ff54c3efee90ae2db6db9Marco Nelissen                        if (isaudio || isvideo || isimage) {
6334887789e44cdb16b042a35e8ec03983213e88ac6Jeff Sharkey                            path = Environment.maybeTranslateEmulatedPathToInternal(new File(path))
6344887789e44cdb16b042a35e8ec03983213e88ac6Jeff Sharkey                                    .getAbsolutePath();
635c861411d4bae0912054ff54c3efee90ae2db6db9Marco Nelissen                        }
636c861411d4bae0912054ff54c3efee90ae2db6db9Marco Nelissen
637997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                        // we only extract metadata for audio and video files
638c861411d4bae0912054ff54c3efee90ae2db6db9Marco Nelissen                        if (isaudio || isvideo) {
639bab75909fac9c65b71012f4c06302503c2f72be1Marco Nelissen                            mScanSuccess = processFile(path, mimeType, this);
640997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                        }
64120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
642c861411d4bae0912054ff54c3efee90ae2db6db9Marco Nelissen                        if (isimage) {
643bab75909fac9c65b71012f4c06302503c2f72be1Marco Nelissen                            mScanSuccess = processImageFile(path);
644e1bf8efaff42dc33b7a4663f3c9d50d12de81bcbChih-Chung Chang                        }
645e1bf8efaff42dc33b7a4663f3c9d50d12de81bcbChih-Chung Chang
646bab75909fac9c65b71012f4c06302503c2f72be1Marco Nelissen                        String lowpath = path.toLowerCase(Locale.ROOT);
647bab75909fac9c65b71012f4c06302503c2f72be1Marco Nelissen                        boolean ringtones = mScanSuccess && (lowpath.indexOf(RINGTONES_DIR) > 0);
648bab75909fac9c65b71012f4c06302503c2f72be1Marco Nelissen                        boolean notifications = mScanSuccess &&
649bab75909fac9c65b71012f4c06302503c2f72be1Marco Nelissen                                (lowpath.indexOf(NOTIFICATIONS_DIR) > 0);
650bab75909fac9c65b71012f4c06302503c2f72be1Marco Nelissen                        boolean alarms = mScanSuccess && (lowpath.indexOf(ALARMS_DIR) > 0);
651bab75909fac9c65b71012f4c06302503c2f72be1Marco Nelissen                        boolean podcasts = mScanSuccess && (lowpath.indexOf(PODCAST_DIR) > 0);
652bab75909fac9c65b71012f4c06302503c2f72be1Marco Nelissen                        boolean music = mScanSuccess && ((lowpath.indexOf(MUSIC_DIR) > 0) ||
653bab75909fac9c65b71012f4c06302503c2f72be1Marco Nelissen                            (!ringtones && !notifications && !alarms && !podcasts));
654bab75909fac9c65b71012f4c06302503c2f72be1Marco Nelissen
655997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                        result = endFile(entry, ringtones, notifications, alarms, music, podcasts);
656997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                    }
6579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
6589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (RemoteException e) {
6599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e);
6609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
6619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project//            long t2 = System.currentTimeMillis();
6629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project//            Log.v(TAG, "scanFile: " + path + " took " + (t2-t1));
6639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return result;
6649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6665f0c3a531938c0c1399efab94906aee74eff9108Bryan Mawhinney        private long parseDate(String date) {
6675f0c3a531938c0c1399efab94906aee74eff9108Bryan Mawhinney            try {
6685f0c3a531938c0c1399efab94906aee74eff9108Bryan Mawhinney              return mDateFormatter.parse(date).getTime();
6695f0c3a531938c0c1399efab94906aee74eff9108Bryan Mawhinney            } catch (ParseException e) {
6705f0c3a531938c0c1399efab94906aee74eff9108Bryan Mawhinney              return 0;
6715f0c3a531938c0c1399efab94906aee74eff9108Bryan Mawhinney            }
6725f0c3a531938c0c1399efab94906aee74eff9108Bryan Mawhinney        }
6735f0c3a531938c0c1399efab94906aee74eff9108Bryan Mawhinney
6749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private int parseSubstring(String s, int start, int defaultValue) {
6759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int length = s.length();
6769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (start == length) return defaultValue;
6779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            char ch = s.charAt(start++);
6799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // return defaultValue if we have no integer at all
6809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (ch < '0' || ch > '9') return defaultValue;
68120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
6829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int result = ch - '0';
6839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            while (start < length) {
6849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                ch = s.charAt(start++);
6859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (ch < '0' || ch > '9') return result;
6869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                result = result * 10 + (ch - '0');
6879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
68820b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
6899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return result;
69020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync        }
69120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
6929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void handleStringTag(String name, String value) {
6939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (name.equalsIgnoreCase("title") || name.startsWith("title;")) {
69473bb511adee5444ecd041146fbbd3677fb635949Marco Nelissen                // Don't trim() here, to preserve the special \001 character
69573bb511adee5444ecd041146fbbd3677fb635949Marco Nelissen                // used to force sorting. The media provider will trim() before
69673bb511adee5444ecd041146fbbd3677fb635949Marco Nelissen                // inserting the title in to the database.
69773bb511adee5444ecd041146fbbd3677fb635949Marco Nelissen                mTitle = value;
6989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (name.equalsIgnoreCase("artist") || name.startsWith("artist;")) {
6999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mArtist = value.trim();
700ed8188711a794867b47d21bbed3e26827f6c27e3Neel Parekh            } else if (name.equalsIgnoreCase("albumartist") || name.startsWith("albumartist;")
701ed8188711a794867b47d21bbed3e26827f6c27e3Neel Parekh                    || name.equalsIgnoreCase("band") || name.startsWith("band;")) {
7029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mAlbumArtist = value.trim();
7039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (name.equalsIgnoreCase("album") || name.startsWith("album;")) {
7049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mAlbum = value.trim();
7059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (name.equalsIgnoreCase("composer") || name.startsWith("composer;")) {
7069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mComposer = value.trim();
70743b076bb75e86fbb34cbe36aa0298c7a647121ffMike Lockwood            } else if (mProcessGenres &&
70843b076bb75e86fbb34cbe36aa0298c7a647121ffMike Lockwood                    (name.equalsIgnoreCase("genre") || name.startsWith("genre;"))) {
709cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                mGenre = getGenreName(value);
7109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (name.equalsIgnoreCase("year") || name.startsWith("year;")) {
7119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mYear = parseSubstring(value, 0, 0);
7129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (name.equalsIgnoreCase("tracknumber") || name.startsWith("tracknumber;")) {
7139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // track number might be of the form "2/12"
7149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // we just read the number before the slash
7159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int num = parseSubstring(value, 0, 0);
71620b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync                mTrack = (mTrack / 1000) * 1000 + num;
7179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (name.equalsIgnoreCase("discnumber") ||
7189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    name.equals("set") || name.startsWith("set;")) {
7199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // set number might be of the form "1/3"
7209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // we just read the number before the slash
7219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int num = parseSubstring(value, 0, 0);
7229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mTrack = (num * 1000) + (mTrack % 1000);
7239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (name.equalsIgnoreCase("duration")) {
7249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mDuration = parseSubstring(value, 0, 0);
725c9706f0262a2f399f95e0d78ba2b7616e08d0069Sahil Sachdeva            } else if (name.equalsIgnoreCase("writer") || name.startsWith("writer;")) {
726c9706f0262a2f399f95e0d78ba2b7616e08d0069Sahil Sachdeva                mWriter = value.trim();
727f679ba7d944a403fbdc77926c44978ffd5161f2dMarco Nelissen            } else if (name.equalsIgnoreCase("compilation")) {
728f679ba7d944a403fbdc77926c44978ffd5161f2dMarco Nelissen                mCompilation = parseSubstring(value, 0, 0);
72982428a862f325238cfb5646bbd65de3f1a11e7ccGloria Wang            } else if (name.equalsIgnoreCase("isdrm")) {
73082428a862f325238cfb5646bbd65de3f1a11e7ccGloria Wang                mIsDrm = (parseSubstring(value, 0, 0) == 1);
7315f0c3a531938c0c1399efab94906aee74eff9108Bryan Mawhinney            } else if (name.equalsIgnoreCase("date")) {
7325f0c3a531938c0c1399efab94906aee74eff9108Bryan Mawhinney                mDate = parseDate(value);
733e313f721ad97ad44666dd066a891acb4ddfa8ab3Marco Nelissen            } else if (name.equalsIgnoreCase("width")) {
734e313f721ad97ad44666dd066a891acb4ddfa8ab3Marco Nelissen                mWidth = parseSubstring(value, 0, 0);
735e313f721ad97ad44666dd066a891acb4ddfa8ab3Marco Nelissen            } else if (name.equalsIgnoreCase("height")) {
736e313f721ad97ad44666dd066a891acb4ddfa8ab3Marco Nelissen                mHeight = parseSubstring(value, 0, 0);
737ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen            } else {
738ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen                //Log.v(TAG, "unknown tag: " + name + " (" + mProcessGenres + ")");
7399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
7409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
74120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
742ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen        private boolean convertGenreCode(String input, String expected) {
743ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen            String output = getGenreName(input);
744ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen            if (output.equals(expected)) {
745ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen                return true;
746ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen            } else {
747ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen                Log.d(TAG, "'" + input + "' -> '" + output + "', expected '" + expected + "'");
748ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen                return false;
749ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen            }
750ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen        }
751ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen        private void testGenreNameConverter() {
752ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen            convertGenreCode("2", "Country");
753ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen            convertGenreCode("(2)", "Country");
754ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen            convertGenreCode("(2", "(2");
755ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen            convertGenreCode("2 Foo", "Country");
756ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen            convertGenreCode("(2) Foo", "Country");
757ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen            convertGenreCode("(2 Foo", "(2 Foo");
758ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen            convertGenreCode("2Foo", "2Foo");
759ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen            convertGenreCode("(2)Foo", "Country");
760ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen            convertGenreCode("200 Foo", "Foo");
761ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen            convertGenreCode("(200) Foo", "Foo");
762ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen            convertGenreCode("200Foo", "200Foo");
763ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen            convertGenreCode("(200)Foo", "Foo");
764ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen            convertGenreCode("200)Foo", "200)Foo");
765ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen            convertGenreCode("200) Foo", "200) Foo");
766ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen        }
767ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen
768cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        public String getGenreName(String genreTagValue) {
769cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen
770cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen            if (genreTagValue == null) {
771cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                return null;
772cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen            }
773cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen            final int length = genreTagValue.length();
774cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen
775ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen            if (length > 0) {
776ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen                boolean parenthesized = false;
777cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                StringBuffer number = new StringBuffer();
778ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen                int i = 0;
779ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen                for (; i < length; ++i) {
780cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                    char c = genreTagValue.charAt(i);
781ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen                    if (i == 0 && c == '(') {
782ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen                        parenthesized = true;
783ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen                    } else if (Character.isDigit(c)) {
784cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                        number.append(c);
785cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                    } else {
786cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                        break;
787cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                    }
788cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                }
789ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen                char charAfterNumber = i < length ? genreTagValue.charAt(i) : ' ';
790ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen                if ((parenthesized && charAfterNumber == ')')
791ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen                        || !parenthesized && Character.isWhitespace(charAfterNumber)) {
792cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                    try {
793cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                        short genreIndex = Short.parseShort(number.toString());
794cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                        if (genreIndex >= 0) {
795c3ea477e2111f199d10341b121791cb2ec91eca3Johan Redestig                            if (genreIndex < ID3_GENRES.length && ID3_GENRES[genreIndex] != null) {
796cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                                return ID3_GENRES[genreIndex];
797cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                            } else if (genreIndex == 0xFF) {
798cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                                return null;
799cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                            } else if (genreIndex < 0xFF && (i + 1) < length) {
800cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                                // genre is valid but unknown,
801cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                                // if there is a string after the value we take it
802ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen                                if (parenthesized && charAfterNumber == ')') {
803ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen                                    i++;
804ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen                                }
805ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen                                String ret = genreTagValue.substring(i).trim();
806ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen                                if (ret.length() != 0) {
807ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen                                    return ret;
808ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen                                }
809cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                            } else {
810cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                                // else return the number, without parentheses
811cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                                return number.toString();
812cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                            }
813cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                        }
814cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                    } catch (NumberFormatException e) {
815cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                    }
816cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                }
817cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen            }
818cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen
819cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen            return genreTagValue;
820cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        }
821cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen
822bab75909fac9c65b71012f4c06302503c2f72be1Marco Nelissen        private boolean processImageFile(String path) {
823e1bf8efaff42dc33b7a4663f3c9d50d12de81bcbChih-Chung Chang            try {
824e1bf8efaff42dc33b7a4663f3c9d50d12de81bcbChih-Chung Chang                mBitmapOptions.outWidth = 0;
825e1bf8efaff42dc33b7a4663f3c9d50d12de81bcbChih-Chung Chang                mBitmapOptions.outHeight = 0;
826e1bf8efaff42dc33b7a4663f3c9d50d12de81bcbChih-Chung Chang                BitmapFactory.decodeFile(path, mBitmapOptions);
827e1bf8efaff42dc33b7a4663f3c9d50d12de81bcbChih-Chung Chang                mWidth = mBitmapOptions.outWidth;
828e1bf8efaff42dc33b7a4663f3c9d50d12de81bcbChih-Chung Chang                mHeight = mBitmapOptions.outHeight;
829bab75909fac9c65b71012f4c06302503c2f72be1Marco Nelissen                return mWidth > 0 && mHeight > 0;
830e1bf8efaff42dc33b7a4663f3c9d50d12de81bcbChih-Chung Chang            } catch (Throwable th) {
831e1bf8efaff42dc33b7a4663f3c9d50d12de81bcbChih-Chung Chang                // ignore;
832e1bf8efaff42dc33b7a4663f3c9d50d12de81bcbChih-Chung Chang            }
833bab75909fac9c65b71012f4c06302503c2f72be1Marco Nelissen            return false;
834e1bf8efaff42dc33b7a4663f3c9d50d12de81bcbChih-Chung Chang        }
835e1bf8efaff42dc33b7a4663f3c9d50d12de81bcbChih-Chung Chang
8369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void setMimeType(String mimeType) {
8371851db6804cbdf8d2c8ede0725e73be6fda0f3c0Marco Nelissen            if ("audio/mp4".equals(mMimeType) &&
8381851db6804cbdf8d2c8ede0725e73be6fda0f3c0Marco Nelissen                    mimeType.startsWith("video")) {
8391851db6804cbdf8d2c8ede0725e73be6fda0f3c0Marco Nelissen                // for feature parity with Donut, we force m4a files to keep the
8401851db6804cbdf8d2c8ede0725e73be6fda0f3c0Marco Nelissen                // audio/mp4 mimetype, even if they are really "enhanced podcasts"
8411851db6804cbdf8d2c8ede0725e73be6fda0f3c0Marco Nelissen                // with a video track
8421851db6804cbdf8d2c8ede0725e73be6fda0f3c0Marco Nelissen                return;
8431851db6804cbdf8d2c8ede0725e73be6fda0f3c0Marco Nelissen            }
8449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mMimeType = mimeType;
8459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mFileType = MediaFile.getFileTypeForMimeType(mimeType);
8469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
84720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
8489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
8499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Formats the data into a values array suitable for use with the Media
8509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Content Provider.
85120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync         *
8529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return a map of values
8539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
8549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private ContentValues toValues() {
8559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ContentValues map = new ContentValues();
8569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            map.put(MediaStore.MediaColumns.DATA, mPath);
8589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            map.put(MediaStore.MediaColumns.TITLE, mTitle);
8599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            map.put(MediaStore.MediaColumns.DATE_MODIFIED, mLastModified);
8609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            map.put(MediaStore.MediaColumns.SIZE, mFileSize);
8619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            map.put(MediaStore.MediaColumns.MIME_TYPE, mMimeType);
86282428a862f325238cfb5646bbd65de3f1a11e7ccGloria Wang            map.put(MediaStore.MediaColumns.IS_DRM, mIsDrm);
86320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
864e313f721ad97ad44666dd066a891acb4ddfa8ab3Marco Nelissen            String resolution = null;
865e1bf8efaff42dc33b7a4663f3c9d50d12de81bcbChih-Chung Chang            if (mWidth > 0 && mHeight > 0) {
866e1bf8efaff42dc33b7a4663f3c9d50d12de81bcbChih-Chung Chang                map.put(MediaStore.MediaColumns.WIDTH, mWidth);
867e1bf8efaff42dc33b7a4663f3c9d50d12de81bcbChih-Chung Chang                map.put(MediaStore.MediaColumns.HEIGHT, mHeight);
868e313f721ad97ad44666dd066a891acb4ddfa8ab3Marco Nelissen                resolution = mWidth + "x" + mHeight;
869e1bf8efaff42dc33b7a4663f3c9d50d12de81bcbChih-Chung Chang            }
870e1bf8efaff42dc33b7a4663f3c9d50d12de81bcbChih-Chung Chang
871e19572ccb5e7963eee9dd1cca150ee752a850e4aMike Lockwood            if (!mNoMedia) {
872997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                if (MediaFile.isVideoFileType(mFileType)) {
873997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                    map.put(Video.Media.ARTIST, (mArtist != null && mArtist.length() > 0
874997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                            ? mArtist : MediaStore.UNKNOWN_STRING));
875997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                    map.put(Video.Media.ALBUM, (mAlbum != null && mAlbum.length() > 0
876997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                            ? mAlbum : MediaStore.UNKNOWN_STRING));
877997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                    map.put(Video.Media.DURATION, mDuration);
878e313f721ad97ad44666dd066a891acb4ddfa8ab3Marco Nelissen                    if (resolution != null) {
879e313f721ad97ad44666dd066a891acb4ddfa8ab3Marco Nelissen                        map.put(Video.Media.RESOLUTION, resolution);
880e313f721ad97ad44666dd066a891acb4ddfa8ab3Marco Nelissen                    }
8815f0c3a531938c0c1399efab94906aee74eff9108Bryan Mawhinney                    if (mDate > 0) {
8825f0c3a531938c0c1399efab94906aee74eff9108Bryan Mawhinney                        map.put(Video.Media.DATE_TAKEN, mDate);
8835f0c3a531938c0c1399efab94906aee74eff9108Bryan Mawhinney                    }
884997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                } else if (MediaFile.isImageFileType(mFileType)) {
885997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                    // FIXME - add DESCRIPTION
886bab75909fac9c65b71012f4c06302503c2f72be1Marco Nelissen                } else if (mScanSuccess && MediaFile.isAudioFileType(mFileType)) {
887997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                    map.put(Audio.Media.ARTIST, (mArtist != null && mArtist.length() > 0) ?
888997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                            mArtist : MediaStore.UNKNOWN_STRING);
889997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                    map.put(Audio.Media.ALBUM_ARTIST, (mAlbumArtist != null &&
890997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                            mAlbumArtist.length() > 0) ? mAlbumArtist : null);
891997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                    map.put(Audio.Media.ALBUM, (mAlbum != null && mAlbum.length() > 0) ?
892997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                            mAlbum : MediaStore.UNKNOWN_STRING);
893997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                    map.put(Audio.Media.COMPOSER, mComposer);
894e5d0495d5206167c597efcd81436a0f881f069f9Mike Lockwood                    map.put(Audio.Media.GENRE, mGenre);
895997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                    if (mYear != 0) {
896997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                        map.put(Audio.Media.YEAR, mYear);
897997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                    }
898997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                    map.put(Audio.Media.TRACK, mTrack);
899997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                    map.put(Audio.Media.DURATION, mDuration);
900997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                    map.put(Audio.Media.COMPILATION, mCompilation);
9019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
902bab75909fac9c65b71012f4c06302503c2f72be1Marco Nelissen                if (!mScanSuccess) {
903bab75909fac9c65b71012f4c06302503c2f72be1Marco Nelissen                    // force mediaprovider to not determine the media type from the mime type
904bab75909fac9c65b71012f4c06302503c2f72be1Marco Nelissen                    map.put(Files.FileColumns.MEDIA_TYPE, 0);
905bab75909fac9c65b71012f4c06302503c2f72be1Marco Nelissen                }
9069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return map;
9089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
90920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
91058ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen        private Uri endFile(FileEntry entry, boolean ringtones, boolean notifications,
91120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync                boolean alarms, boolean music, boolean podcasts)
9129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                throws RemoteException {
9139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // update database
91420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
915997354e4b4a9666cedd62282471e97822affced9Mike Lockwood            // use album artist if artist is missing
9169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mArtist == null || mArtist.length() == 0) {
9179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mArtist = mAlbumArtist;
9189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ContentValues values = toValues();
9219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String title = values.getAsString(MediaStore.MediaColumns.TITLE);
922bdc0e82f9c075edb67da4a2583e557824c280415Mike Lockwood            if (title == null || TextUtils.isEmpty(title.trim())) {
923c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood                title = MediaFile.getFileTitle(values.getAsString(MediaStore.MediaColumns.DATA));
9249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                values.put(MediaStore.MediaColumns.TITLE, title);
9259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
926c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen            String album = values.getAsString(Audio.Media.ALBUM);
9279a488b44cb479bc42375d2cfcdd4aa6161dc17cfMarco Nelissen            if (MediaStore.UNKNOWN_STRING.equals(album)) {
928c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                album = values.getAsString(MediaStore.MediaColumns.DATA);
929c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                // extract last path segment before file name
930c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                int lastSlash = album.lastIndexOf('/');
931c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                if (lastSlash >= 0) {
932c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                    int previousSlash = 0;
933c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                    while (true) {
934c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                        int idx = album.indexOf('/', previousSlash + 1);
935c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                        if (idx < 0 || idx >= lastSlash) {
936c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                            break;
937c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                        }
938c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                        previousSlash = idx;
939c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                    }
940c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                    if (previousSlash != 0) {
941c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                        album = album.substring(previousSlash + 1, lastSlash);
942c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                        values.put(Audio.Media.ALBUM, album);
943c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                    }
944c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                }
945c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen            }
946c066557378c2305625a7bad3d25f084ad647ef27Marco Nelissen            long rowId = entry.mRowId;
947c2206a0710a3ca2c29854c1a0638c5ba55629ba2Mike Lockwood            if (MediaFile.isAudioFileType(mFileType) && (rowId == 0 || mMtpObjectHandle != 0)) {
948c066557378c2305625a7bad3d25f084ad647ef27Marco Nelissen                // Only set these for new entries. For existing entries, they
949c066557378c2305625a7bad3d25f084ad647ef27Marco Nelissen                // may have been modified later, and we want to keep the current
950c066557378c2305625a7bad3d25f084ad647ef27Marco Nelissen                // values so that custom ringtones still show up in the ringtone
951c066557378c2305625a7bad3d25f084ad647ef27Marco Nelissen                // picker.
9529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                values.put(Audio.Media.IS_RINGTONE, ringtones);
9539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                values.put(Audio.Media.IS_NOTIFICATION, notifications);
9549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                values.put(Audio.Media.IS_ALARM, alarms);
9559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                values.put(Audio.Media.IS_MUSIC, music);
9569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                values.put(Audio.Media.IS_PODCAST, podcasts);
9575a8b9627c7fdacc94791fb3024386739f8cf9a56Jaesung Chung            } else if ((mFileType == MediaFile.FILE_TYPE_JPEG
9583edfdab205ea061493d25284cd720665c285759bChong Zhang                    || mFileType == MediaFile.FILE_TYPE_HEIF
9595a8b9627c7fdacc94791fb3024386739f8cf9a56Jaesung Chung                    || MediaFile.isRawImageFileType(mFileType)) && !mNoMedia) {
960700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang                ExifInterface exif = null;
961700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang                try {
962700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang                    exif = new ExifInterface(entry.mPath);
963700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang                } catch (IOException ex) {
964700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang                    // exif is null
965700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang                }
966700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang                if (exif != null) {
967872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang                    float[] latlng = new float[2];
968872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang                    if (exif.getLatLong(latlng)) {
96920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync                        values.put(Images.Media.LATITUDE, latlng[0]);
97020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync                        values.put(Images.Media.LONGITUDE, latlng[1]);
97120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync                    }
972099397cbd07c8c991f3126d0d0ac64bb6b3c0b47Chih-Chung Chang
9733a8cab88e7747dc280ce85895af014f98e80a6ccRay Chen                    long time = exif.getGpsDateTime();
974099397cbd07c8c991f3126d0d0ac64bb6b3c0b47Chih-Chung Chang                    if (time != -1) {
975052d9c8b414dc9eb1c70d849660592d9bc3b2644Ray Chen                        values.put(Images.Media.DATE_TAKEN, time);
976a2c322f599b5a44ca153922b994a05a4311331cbRay Chen                    } else {
977a2c322f599b5a44ca153922b994a05a4311331cbRay Chen                        // If no time zone information is available, we should consider using
978a2c322f599b5a44ca153922b994a05a4311331cbRay Chen                        // EXIF local time as taken time if the difference between file time
979a2c322f599b5a44ca153922b994a05a4311331cbRay Chen                        // and EXIF local time is not less than 1 Day, otherwise MediaProvider
980a2c322f599b5a44ca153922b994a05a4311331cbRay Chen                        // will use file time as taken time.
981a2c322f599b5a44ca153922b994a05a4311331cbRay Chen                        time = exif.getDateTime();
982872e7b7073fad8295e6314c73b87d7cb3f85d39cRay Chen                        if (time != -1 && Math.abs(mLastModified * 1000 - time) >= 86400000) {
983a2c322f599b5a44ca153922b994a05a4311331cbRay Chen                            values.put(Images.Media.DATE_TAKEN, time);
984a2c322f599b5a44ca153922b994a05a4311331cbRay Chen                        }
985099397cbd07c8c991f3126d0d0ac64bb6b3c0b47Chih-Chung Chang                    }
986b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang
987b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                    int orientation = exif.getAttributeInt(
988b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                        ExifInterface.TAG_ORIENTATION, -1);
989b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                    if (orientation != -1) {
990b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                        // We only recognize a subset of orientation tag values.
991b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                        int degree;
992b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                        switch(orientation) {
993b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                            case ExifInterface.ORIENTATION_ROTATE_90:
994b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                                degree = 90;
995b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                                break;
996b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                            case ExifInterface.ORIENTATION_ROTATE_180:
997b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                                degree = 180;
998b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                                break;
999b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                            case ExifInterface.ORIENTATION_ROTATE_270:
1000b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                                degree = 270;
1001b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                                break;
1002b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                            default:
1003b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                                degree = 0;
1004b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                                break;
1005b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                        }
1006b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                        values.put(Images.Media.ORIENTATION, degree);
1007b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                    }
100820b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync                }
10099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
101020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
101145aaa2a345292bb6d600822db602f25e261c0e50Mike Lockwood            Uri tableUri = mFilesUri;
10128e2ed8d512ade2ea481b3a292556782b17a74674Dongwon Kang            MediaInserter inserter = mMediaInserter;
1013bab75909fac9c65b71012f4c06302503c2f72be1Marco Nelissen            if (mScanSuccess && !mNoMedia) {
1014e19572ccb5e7963eee9dd1cca150ee752a850e4aMike Lockwood                if (MediaFile.isVideoFileType(mFileType)) {
1015e19572ccb5e7963eee9dd1cca150ee752a850e4aMike Lockwood                    tableUri = mVideoUri;
1016e19572ccb5e7963eee9dd1cca150ee752a850e4aMike Lockwood                } else if (MediaFile.isImageFileType(mFileType)) {
1017e19572ccb5e7963eee9dd1cca150ee752a850e4aMike Lockwood                    tableUri = mImagesUri;
1018e19572ccb5e7963eee9dd1cca150ee752a850e4aMike Lockwood                } else if (MediaFile.isAudioFileType(mFileType)) {
1019e19572ccb5e7963eee9dd1cca150ee752a850e4aMike Lockwood                    tableUri = mAudioUri;
1020e19572ccb5e7963eee9dd1cca150ee752a850e4aMike Lockwood                }
1021e19572ccb5e7963eee9dd1cca150ee752a850e4aMike Lockwood            }
10229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Uri result = null;
10236c4c15569674e6a5eda0565b501b600a50d4ca6eSungsoo Lim            boolean needToSetSettings = false;
102468528911b9f74475ac79c369a6482f720259dbacMarco Nelissen            // Setting a flag in order not to use bulk insert for the file related with
102568528911b9f74475ac79c369a6482f720259dbacMarco Nelissen            // notifications, ringtones, and alarms, because the rowId of the inserted file is
102668528911b9f74475ac79c369a6482f720259dbacMarco Nelissen            // needed.
102768528911b9f74475ac79c369a6482f720259dbacMarco Nelissen            if (notifications && !mDefaultNotificationSet) {
102868528911b9f74475ac79c369a6482f720259dbacMarco Nelissen                if (TextUtils.isEmpty(mDefaultNotificationFilename) ||
102968528911b9f74475ac79c369a6482f720259dbacMarco Nelissen                        doesPathHaveFilename(entry.mPath, mDefaultNotificationFilename)) {
103068528911b9f74475ac79c369a6482f720259dbacMarco Nelissen                    needToSetSettings = true;
103168528911b9f74475ac79c369a6482f720259dbacMarco Nelissen                }
103268528911b9f74475ac79c369a6482f720259dbacMarco Nelissen            } else if (ringtones && !mDefaultRingtoneSet) {
103368528911b9f74475ac79c369a6482f720259dbacMarco Nelissen                if (TextUtils.isEmpty(mDefaultRingtoneFilename) ||
103468528911b9f74475ac79c369a6482f720259dbacMarco Nelissen                        doesPathHaveFilename(entry.mPath, mDefaultRingtoneFilename)) {
103568528911b9f74475ac79c369a6482f720259dbacMarco Nelissen                    needToSetSettings = true;
103668528911b9f74475ac79c369a6482f720259dbacMarco Nelissen                }
103768528911b9f74475ac79c369a6482f720259dbacMarco Nelissen            } else if (alarms && !mDefaultAlarmSet) {
103868528911b9f74475ac79c369a6482f720259dbacMarco Nelissen                if (TextUtils.isEmpty(mDefaultAlarmAlertFilename) ||
103968528911b9f74475ac79c369a6482f720259dbacMarco Nelissen                        doesPathHaveFilename(entry.mPath, mDefaultAlarmAlertFilename)) {
104068528911b9f74475ac79c369a6482f720259dbacMarco Nelissen                    needToSetSettings = true;
104168528911b9f74475ac79c369a6482f720259dbacMarco Nelissen                }
104268528911b9f74475ac79c369a6482f720259dbacMarco Nelissen            }
104368528911b9f74475ac79c369a6482f720259dbacMarco Nelissen
10449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (rowId == 0) {
1045c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood                if (mMtpObjectHandle != 0) {
1046c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood                    values.put(MediaStore.MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID, mMtpObjectHandle);
1047c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood                }
1048e19572ccb5e7963eee9dd1cca150ee752a850e4aMike Lockwood                if (tableUri == mFilesUri) {
1049e19572ccb5e7963eee9dd1cca150ee752a850e4aMike Lockwood                    int format = entry.mFormat;
1050e19572ccb5e7963eee9dd1cca150ee752a850e4aMike Lockwood                    if (format == 0) {
1051e19572ccb5e7963eee9dd1cca150ee752a850e4aMike Lockwood                        format = MediaFile.getFormatCode(entry.mPath, mMimeType);
1052e19572ccb5e7963eee9dd1cca150ee752a850e4aMike Lockwood                    }
1053e19572ccb5e7963eee9dd1cca150ee752a850e4aMike Lockwood                    values.put(Files.FileColumns.FORMAT, format);
1054c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood                }
1055d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissen                // New file, insert it.
1056d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissen                // Directories need to be inserted before the files they contain, so they
1057d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissen                // get priority when bulk inserting.
1058d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissen                // If the rowId of the inserted file is needed, it gets inserted immediately,
1059d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissen                // bypassing the bulk inserter.
1060d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissen                if (inserter == null || needToSetSettings) {
1061299942002b17f792e6e4f6b3f6972f62b8b136aeMarco Nelissen                    if (inserter != null) {
1062299942002b17f792e6e4f6b3f6972f62b8b136aeMarco Nelissen                        inserter.flushAll();
1063299942002b17f792e6e4f6b3f6972f62b8b136aeMarco Nelissen                    }
106460cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey                    result = mMediaProvider.insert(tableUri, values);
1065d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissen                } else if (entry.mFormat == MtpConstants.FORMAT_ASSOCIATION) {
1066d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissen                    inserter.insertwithPriority(tableUri, values);
1067c6eaa713a3f5b9337eca60ba2ab570cf6e80c357Mike Lockwood                } else {
10688e2ed8d512ade2ea481b3a292556782b17a74674Dongwon Kang                    inserter.insert(tableUri, values);
1069bd682b040833fce9e212c00c395b32bec7050f87Mike Lockwood                }
1070bd682b040833fce9e212c00c395b32bec7050f87Mike Lockwood
10719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (result != null) {
10729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    rowId = ContentUris.parseId(result);
10739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    entry.mRowId = rowId;
10749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
10759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
10769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // updated file
107745aaa2a345292bb6d600822db602f25e261c0e50Mike Lockwood                result = ContentUris.withAppendedId(tableUri, rowId);
10780dd9f4de3ed681e83bd79ddcd4fd463093d11ed3Mike Lockwood                // path should never change, and we want to avoid replacing mixed cased paths
10790dd9f4de3ed681e83bd79ddcd4fd463093d11ed3Mike Lockwood                // with squashed lower case paths
10800dd9f4de3ed681e83bd79ddcd4fd463093d11ed3Mike Lockwood                values.remove(MediaStore.MediaColumns.DATA);
1081ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen
1082ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen                int mediaType = 0;
1083bab75909fac9c65b71012f4c06302503c2f72be1Marco Nelissen                if (mScanSuccess && !MediaScanner.isNoMediaPath(entry.mPath)) {
1084ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen                    int fileType = MediaFile.getFileTypeForMimeType(mMimeType);
1085ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen                    if (MediaFile.isAudioFileType(fileType)) {
1086ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen                        mediaType = FileColumns.MEDIA_TYPE_AUDIO;
1087ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen                    } else if (MediaFile.isVideoFileType(fileType)) {
1088ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen                        mediaType = FileColumns.MEDIA_TYPE_VIDEO;
1089ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen                    } else if (MediaFile.isImageFileType(fileType)) {
1090ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen                        mediaType = FileColumns.MEDIA_TYPE_IMAGE;
1091ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen                    } else if (MediaFile.isPlayListFileType(fileType)) {
1092ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen                        mediaType = FileColumns.MEDIA_TYPE_PLAYLIST;
1093ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen                    }
1094ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen                    values.put(FileColumns.MEDIA_TYPE, mediaType);
1095ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen                }
109660cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey                mMediaProvider.update(result, values, null, null);
10979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
109820b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
10996c4c15569674e6a5eda0565b501b600a50d4ca6eSungsoo Lim            if(needToSetSettings) {
11006c4c15569674e6a5eda0565b501b600a50d4ca6eSungsoo Lim                if (notifications) {
11019dd3b2162dd308762a023ad07a39c2379bcbf931Jeff Sharkey                    setRingtoneIfNotSet(Settings.System.NOTIFICATION_SOUND, tableUri, rowId);
11029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    mDefaultNotificationSet = true;
11036c4c15569674e6a5eda0565b501b600a50d4ca6eSungsoo Lim                } else if (ringtones) {
11049dd3b2162dd308762a023ad07a39c2379bcbf931Jeff Sharkey                    setRingtoneIfNotSet(Settings.System.RINGTONE, tableUri, rowId);
11059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    mDefaultRingtoneSet = true;
11066c4c15569674e6a5eda0565b501b600a50d4ca6eSungsoo Lim                } else if (alarms) {
11079dd3b2162dd308762a023ad07a39c2379bcbf931Jeff Sharkey                    setRingtoneIfNotSet(Settings.System.ALARM_ALERT, tableUri, rowId);
11083156bb00dd3ab3d6ec732bb6a64a2e84d08c34f0Patrick Scott                    mDefaultAlarmSet = true;
11093156bb00dd3ab3d6ec732bb6a64a2e84d08c34f0Patrick Scott                }
11109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
111120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
11129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return result;
11139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
111420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
11159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private boolean doesPathHaveFilename(String path, String filename) {
11169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int pathFilenameStart = path.lastIndexOf(File.separatorChar) + 1;
11179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int filenameLength = filename.length();
11189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return path.regionMatches(pathFilenameStart, filename, 0, filenameLength) &&
11199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    pathFilenameStart + filenameLength == path.length();
11209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
112120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
11229dd3b2162dd308762a023ad07a39c2379bcbf931Jeff Sharkey        private void setRingtoneIfNotSet(String settingName, Uri uri, long rowId) {
11239dd3b2162dd308762a023ad07a39c2379bcbf931Jeff Sharkey            if (wasRingtoneAlreadySet(settingName)) {
112468528911b9f74475ac79c369a6482f720259dbacMarco Nelissen                return;
112568528911b9f74475ac79c369a6482f720259dbacMarco Nelissen            }
112620b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
112768528911b9f74475ac79c369a6482f720259dbacMarco Nelissen            ContentResolver cr = mContext.getContentResolver();
112868528911b9f74475ac79c369a6482f720259dbacMarco Nelissen            String existingSettingValue = Settings.System.getString(cr, settingName);
11299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (TextUtils.isEmpty(existingSettingValue)) {
11309dd3b2162dd308762a023ad07a39c2379bcbf931Jeff Sharkey                final Uri settingUri = Settings.System.getUriFor(settingName);
11319dd3b2162dd308762a023ad07a39c2379bcbf931Jeff Sharkey                final Uri ringtoneUri = ContentUris.withAppendedId(uri, rowId);
11329dd3b2162dd308762a023ad07a39c2379bcbf931Jeff Sharkey                RingtoneManager.setActualDefaultRingtoneUri(mContext,
11339dd3b2162dd308762a023ad07a39c2379bcbf931Jeff Sharkey                        RingtoneManager.getDefaultType(settingUri), ringtoneUri);
11349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
113568528911b9f74475ac79c369a6482f720259dbacMarco Nelissen            Settings.System.putInt(cr, settingSetIndicatorName(settingName), 1);
11369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
113720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
11385d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang        private int getFileTypeFromDrm(String path) {
1139e4eb4d9de37a8e9ed6f43a5f7deccf88f28bfc8bMarco Nelissen            if (!isDrmEnabled()) {
11405d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang                return 0;
11415d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang            }
11425d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang
11435d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang            int resultFileType = 0;
11445d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang
11455d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang            if (mDrmManagerClient == null) {
11465d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang                mDrmManagerClient = new DrmManagerClient(mContext);
11475d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang            }
11485d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang
11495d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang            if (mDrmManagerClient.canHandle(path, null)) {
115059c14048bfc0a210077192410afa28ed6f85daa0Erik Almqvist                mIsDrm = true;
11515d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang                String drmMimetype = mDrmManagerClient.getOriginalMimeType(path);
11525d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang                if (drmMimetype != null) {
11535d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang                    mMimeType = drmMimetype;
11545d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang                    resultFileType = MediaFile.getFileTypeForMimeType(drmMimetype);
11555d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang                }
11565d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang            }
11575d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang            return resultFileType;
11585d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang        }
11595d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang
11609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }; // end of anonymous MediaScannerClient instance
116120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
116217d24400906235a172fc760e98656589692f798aJean-Michel Trivi    private static boolean isSystemSoundWithMetadata(String path) {
116317d24400906235a172fc760e98656589692f798aJean-Michel Trivi        if (path.startsWith(SYSTEM_SOUNDS_DIR + ALARMS_DIR)
116417d24400906235a172fc760e98656589692f798aJean-Michel Trivi                || path.startsWith(SYSTEM_SOUNDS_DIR + RINGTONES_DIR)
1165c31033f43c85a07ebc6045c9b5af5408d0b306e9Jaekyun Seok                || path.startsWith(SYSTEM_SOUNDS_DIR + NOTIFICATIONS_DIR)
1166c31033f43c85a07ebc6045c9b5af5408d0b306e9Jaekyun Seok                || path.startsWith(PRODUCT_SOUNDS_DIR + ALARMS_DIR)
1167c31033f43c85a07ebc6045c9b5af5408d0b306e9Jaekyun Seok                || path.startsWith(PRODUCT_SOUNDS_DIR + RINGTONES_DIR)
1168c31033f43c85a07ebc6045c9b5af5408d0b306e9Jaekyun Seok                || path.startsWith(PRODUCT_SOUNDS_DIR + NOTIFICATIONS_DIR)) {
116917d24400906235a172fc760e98656589692f798aJean-Michel Trivi            return true;
117017d24400906235a172fc760e98656589692f798aJean-Michel Trivi        }
117117d24400906235a172fc760e98656589692f798aJean-Michel Trivi        return false;
117217d24400906235a172fc760e98656589692f798aJean-Michel Trivi    }
117317d24400906235a172fc760e98656589692f798aJean-Michel Trivi
117468528911b9f74475ac79c369a6482f720259dbacMarco Nelissen    private String settingSetIndicatorName(String base) {
117568528911b9f74475ac79c369a6482f720259dbacMarco Nelissen        return base + "_set";
117668528911b9f74475ac79c369a6482f720259dbacMarco Nelissen    }
117768528911b9f74475ac79c369a6482f720259dbacMarco Nelissen
11789dd3b2162dd308762a023ad07a39c2379bcbf931Jeff Sharkey    private boolean wasRingtoneAlreadySet(String name) {
117968528911b9f74475ac79c369a6482f720259dbacMarco Nelissen        ContentResolver cr = mContext.getContentResolver();
118068528911b9f74475ac79c369a6482f720259dbacMarco Nelissen        String indicatorName = settingSetIndicatorName(name);
118168528911b9f74475ac79c369a6482f720259dbacMarco Nelissen        try {
118268528911b9f74475ac79c369a6482f720259dbacMarco Nelissen            return Settings.System.getInt(cr, indicatorName) != 0;
118368528911b9f74475ac79c369a6482f720259dbacMarco Nelissen        } catch (SettingNotFoundException e) {
118468528911b9f74475ac79c369a6482f720259dbacMarco Nelissen            return false;
118568528911b9f74475ac79c369a6482f720259dbacMarco Nelissen        }
118668528911b9f74475ac79c369a6482f720259dbacMarco Nelissen    }
118768528911b9f74475ac79c369a6482f720259dbacMarco Nelissen
1188c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood    private void prescan(String filePath, boolean prescanFiles) throws RemoteException {
11899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Cursor c = null;
11909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String where = null;
11919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String[] selectionArgs = null;
119220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
119360cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey        mPlayLists.clear();
119420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
1195c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood        if (filePath != null) {
1196c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood            // query for only one file
119758ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen            where = MediaStore.Files.FileColumns._ID + ">?" +
119858ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                " AND " + Files.FileColumns.DATA + "=?";
119958ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen            selectionArgs = new String[] { "", filePath };
120058ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen        } else {
120158ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen            where = MediaStore.Files.FileColumns._ID + ">?";
120258ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen            selectionArgs = new String[] { "" };
1203c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood        }
1204c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood
12059dd3b2162dd308762a023ad07a39c2379bcbf931Jeff Sharkey        mDefaultRingtoneSet = wasRingtoneAlreadySet(Settings.System.RINGTONE);
12069dd3b2162dd308762a023ad07a39c2379bcbf931Jeff Sharkey        mDefaultNotificationSet = wasRingtoneAlreadySet(Settings.System.NOTIFICATION_SOUND);
12079dd3b2162dd308762a023ad07a39c2379bcbf931Jeff Sharkey        mDefaultAlarmSet = wasRingtoneAlreadySet(Settings.System.ALARM_ALERT);
120868528911b9f74475ac79c369a6482f720259dbacMarco Nelissen
120958ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen        // Tell the provider to not delete the file.
121058ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen        // If the file is truly gone the delete is unnecessary, and we want to avoid
121158ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen        // accidentally deleting files that are really there (this may happen if the
121258ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen        // filesystem is mounted and unmounted while the scanner is running).
121358ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen        Uri.Builder builder = mFilesUri.buildUpon();
121458ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen        builder.appendQueryParameter(MediaStore.PARAM_DELETE_DATA, "false");
121560cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey        MediaBulkDeleter deleter = new MediaBulkDeleter(mMediaProvider, builder.build());
121658ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen
12179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Build the list of files from the content provider
12189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
1219c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood            if (prescanFiles) {
122058ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                // First read existing files from the files table.
122158ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                // Because we'll be deleting entries for missing files as we go,
122258ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                // we need to query the database in small batches, to avoid problems
122358ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                // with CursorWindow positioning.
122458ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                long lastId = Long.MIN_VALUE;
122558ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                Uri limitUri = mFilesUri.buildUpon().appendQueryParameter("limit", "1000").build();
122658ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen
122758ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                while (true) {
122858ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                    selectionArgs[0] = "" + lastId;
122958ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                    if (c != null) {
123058ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                        c.close();
123158ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                        c = null;
123258ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                    }
123360cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey                    c = mMediaProvider.query(limitUri, FILES_PRESCAN_PROJECTION,
123458ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                            where, selectionArgs, MediaStore.Files.FileColumns._ID, null);
123558ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                    if (c == null) {
123658ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                        break;
123758ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                    }
123820b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
123958ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                    int num = c.getCount();
1240c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood
124158ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                    if (num == 0) {
124258ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                        break;
124358ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                    }
12449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    while (c.moveToNext()) {
1245d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood                        long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX);
1246d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood                        String path = c.getString(FILES_PRESCAN_PATH_COLUMN_INDEX);
1247d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood                        int format = c.getInt(FILES_PRESCAN_FORMAT_COLUMN_INDEX);
1248d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood                        long lastModified = c.getLong(FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
124958ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                        lastId = rowId;
125020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
125135b002a9c873fb40116179c8626b9841b0abae74Marco Nelissen                        // Only consider entries with absolute path names.
125235b002a9c873fb40116179c8626b9841b0abae74Marco Nelissen                        // This allows storing URIs in the database without the
125335b002a9c873fb40116179c8626b9841b0abae74Marco Nelissen                        // media scanner removing them.
12543e5e8055942ee8179675b8ed11d40ca12dbe42e6Marco Nelissen                        if (path != null && path.startsWith("/")) {
125558ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                            boolean exists = false;
125658ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                            try {
125734385d352da19805ae948215e2edbeedd16b7941Elliott Hughes                                exists = Os.access(path, android.system.OsConstants.F_OK);
125858ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                            } catch (ErrnoException e1) {
125958ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                            }
126058ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                            if (!exists && !MtpConstants.isAbstractObject(format)) {
126158ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                                // do not delete missing playlists, since they may have been
126258ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                                // modified by the user.
126358ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                                // The user can delete them in the media player instead.
126458ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                                // instead, clear the path and lastModified fields in the row
126558ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                                MediaFile.MediaFileType mediaFileType = MediaFile.getFileType(path);
126658ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                                int fileType = (mediaFileType == null ? 0 : mediaFileType.fileType);
126758ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen
126858ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                                if (!MediaFile.isPlayListFileType(fileType)) {
126958ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                                    deleter.delete(rowId);
127058ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                                    if (path.toLowerCase(Locale.US).endsWith("/.nomedia")) {
127158ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                                        deleter.flush();
127258ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                                        String parent = new File(path).getParent();
127360cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey                                        mMediaProvider.call(MediaStore.UNHIDE_CALL, parent, null);
127458ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                                    }
127558ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                                }
127635b002a9c873fb40116179c8626b9841b0abae74Marco Nelissen                            }
12779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
12789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
12799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
12809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
12819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        finally {
12839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (c != null) {
12849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                c.close();
12859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
128658ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen            deleter.flush();
12879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12886458935331ccfacb4cccc50d6b893947febae687Mike Lockwood
12896458935331ccfacb4cccc50d6b893947febae687Mike Lockwood        // compute original size of images
12906458935331ccfacb4cccc50d6b893947febae687Mike Lockwood        mOriginalCount = 0;
129160cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey        c = mMediaProvider.query(mImagesUri, ID_PROJECTION, null, null, null, null);
12926458935331ccfacb4cccc50d6b893947febae687Mike Lockwood        if (c != null) {
12936458935331ccfacb4cccc50d6b893947febae687Mike Lockwood            mOriginalCount = c.getCount();
12946458935331ccfacb4cccc50d6b893947febae687Mike Lockwood            c.close();
12956458935331ccfacb4cccc50d6b893947febae687Mike Lockwood        }
12969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
129720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
12989ff4774cac499a376a59373d5bfb5112c9a2a004Marco Nelissen    static class MediaBulkDeleter {
1299d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissen        StringBuilder whereClause = new StringBuilder();
1300872e7b7073fad8295e6314c73b87d7cb3f85d39cRay Chen        ArrayList<String> whereArgs = new ArrayList<String>(100);
130160cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey        final ContentProviderClient mProvider;
130235654b61e8fe7bc85afcb076ddbb590d51c5865fDianne Hackborn        final Uri mBaseUri;
13039ff4774cac499a376a59373d5bfb5112c9a2a004Marco Nelissen
130460cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey        public MediaBulkDeleter(ContentProviderClient provider, Uri baseUri) {
13059ff4774cac499a376a59373d5bfb5112c9a2a004Marco Nelissen            mProvider = provider;
13069ff4774cac499a376a59373d5bfb5112c9a2a004Marco Nelissen            mBaseUri = baseUri;
13079ff4774cac499a376a59373d5bfb5112c9a2a004Marco Nelissen        }
13089ff4774cac499a376a59373d5bfb5112c9a2a004Marco Nelissen
13099ff4774cac499a376a59373d5bfb5112c9a2a004Marco Nelissen        public void delete(long id) throws RemoteException {
1310d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissen            if (whereClause.length() != 0) {
1311d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissen                whereClause.append(",");
13129ff4774cac499a376a59373d5bfb5112c9a2a004Marco Nelissen            }
1313d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissen            whereClause.append("?");
1314d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissen            whereArgs.add("" + id);
1315d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissen            if (whereArgs.size() > 100) {
13169ff4774cac499a376a59373d5bfb5112c9a2a004Marco Nelissen                flush();
13179ff4774cac499a376a59373d5bfb5112c9a2a004Marco Nelissen            }
13189ff4774cac499a376a59373d5bfb5112c9a2a004Marco Nelissen        }
13199ff4774cac499a376a59373d5bfb5112c9a2a004Marco Nelissen        public void flush() throws RemoteException {
1320d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissen            int size = whereArgs.size();
1321d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissen            if (size > 0) {
1322d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissen                String [] foo = new String [size];
1323d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissen                foo = whereArgs.toArray(foo);
132460cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey                int numrows = mProvider.delete(mBaseUri,
132535654b61e8fe7bc85afcb076ddbb590d51c5865fDianne Hackborn                        MediaStore.MediaColumns._ID + " IN (" +
1326d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissen                        whereClause.toString() + ")", foo);
1327d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissen                //Log.i("@@@@@@@@@", "rows deleted: " + numrows);
1328d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissen                whereClause.setLength(0);
1329d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissen                whereArgs.clear();
1330d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissen            }
13319ff4774cac499a376a59373d5bfb5112c9a2a004Marco Nelissen        }
13329ff4774cac499a376a59373d5bfb5112c9a2a004Marco Nelissen    }
13339ff4774cac499a376a59373d5bfb5112c9a2a004Marco Nelissen
133468528911b9f74475ac79c369a6482f720259dbacMarco Nelissen    private void postscan(final String[] directories) throws RemoteException {
133520b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
13369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // handle playlists last, after we know what media files are on the storage.
13379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mProcessPlaylists) {
13389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            processPlayLists();
13399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
134020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
13419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // allow GC to clean up
134260cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey        mPlayLists.clear();
13439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
134420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
13451b37c3bddcdfe7cb60e776e3eb05bfbb46b0b282Mattias Nilsson    private void releaseResources() {
13461b37c3bddcdfe7cb60e776e3eb05bfbb46b0b282Mattias Nilsson        // release the DrmManagerClient resources
13471b37c3bddcdfe7cb60e776e3eb05bfbb46b0b282Mattias Nilsson        if (mDrmManagerClient != null) {
134860cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey            mDrmManagerClient.close();
13491b37c3bddcdfe7cb60e776e3eb05bfbb46b0b282Mattias Nilsson            mDrmManagerClient = null;
13501b37c3bddcdfe7cb60e776e3eb05bfbb46b0b282Mattias Nilsson        }
13511b37c3bddcdfe7cb60e776e3eb05bfbb46b0b282Mattias Nilsson    }
13521b37c3bddcdfe7cb60e776e3eb05bfbb46b0b282Mattias Nilsson
135360cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey    public void scanDirectories(String[] directories) {
13549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
13559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            long start = System.currentTimeMillis();
1356c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood            prescan(null, true);
13579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            long prescan = System.currentTimeMillis();
1358e19572ccb5e7963eee9dd1cca150ee752a850e4aMike Lockwood
13593410b4656505673c72da558dcfec94d7b135e760Mike Lockwood            if (ENABLE_BULK_INSERTS) {
13608e2ed8d512ade2ea481b3a292556782b17a74674Dongwon Kang                // create MediaInserter for bulk inserts
136160cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey                mMediaInserter = new MediaInserter(mMediaProvider, 500);
13623410b4656505673c72da558dcfec94d7b135e760Mike Lockwood            }
136320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
13649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < directories.length; i++) {
1365c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood                processDirectory(directories[i], mClient);
13669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1367e19572ccb5e7963eee9dd1cca150ee752a850e4aMike Lockwood
13683410b4656505673c72da558dcfec94d7b135e760Mike Lockwood            if (ENABLE_BULK_INSERTS) {
13693410b4656505673c72da558dcfec94d7b135e760Mike Lockwood                // flush remaining inserts
13708e2ed8d512ade2ea481b3a292556782b17a74674Dongwon Kang                mMediaInserter.flushAll();
13718e2ed8d512ade2ea481b3a292556782b17a74674Dongwon Kang                mMediaInserter = null;
13723410b4656505673c72da558dcfec94d7b135e760Mike Lockwood            }
1373bd682b040833fce9e212c00c395b32bec7050f87Mike Lockwood
13749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            long scan = System.currentTimeMillis();
13759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            postscan(directories);
13769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            long end = System.currentTimeMillis();
137720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
137843a17654cf4bfe7f1ec22bd8b7b32daccdf27c09Joe Onorato            if (false) {
13799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Log.d(TAG, " prescan time: " + (prescan - start) + "ms\n");
13809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Log.d(TAG, "    scan time: " + (scan - prescan) + "ms\n");
13819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Log.d(TAG, "postscan time: " + (end - scan) + "ms\n");
13829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Log.d(TAG, "   total time: " + (end - start) + "ms\n");
13839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
13849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (SQLException e) {
13859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // this might happen if the SD card is removed while the media scanner is running
13869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.e(TAG, "SQLException in MediaScanner.scan()", e);
13879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (UnsupportedOperationException e) {
13889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // this might happen if the SD card is removed while the media scanner is running
13899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.e(TAG, "UnsupportedOperationException in MediaScanner.scan()", e);
13909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (RemoteException e) {
13919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.e(TAG, "RemoteException in MediaScanner.scan()", e);
13921b37c3bddcdfe7cb60e776e3eb05bfbb46b0b282Mattias Nilsson        } finally {
13931b37c3bddcdfe7cb60e776e3eb05bfbb46b0b282Mattias Nilsson            releaseResources();
13949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
13969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // this function is used to scan a single file
139860cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey    public Uri scanSingleFile(String path, String mimeType) {
13999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
1400c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood            prescan(path, true);
140120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
14029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            File file = new File(path);
14036124cddc629eb0a13e9d9e30e0613614d17950c5Bartosz Bialek            if (!file.exists() || !file.canRead()) {
1404348aa65e93c1b8cb409de12e3e61136f894201f2Marco Nelissen                return null;
1405348aa65e93c1b8cb409de12e3e61136f894201f2Marco Nelissen            }
1406ec74abb71f79cdc573c536115acc7ff26a02680aMikael
1407ec74abb71f79cdc573c536115acc7ff26a02680aMikael            // lastModified is in milliseconds on Files.
1408ec74abb71f79cdc573c536115acc7ff26a02680aMikael            long lastModifiedSeconds = file.lastModified() / 1000;
1409ec74abb71f79cdc573c536115acc7ff26a02680aMikael
14109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // always scan the file, so we can return the content://media Uri for existing files
14117a0bd17bceaf3efc3732e30c538fae420d3b742cMike Lockwood            return mClient.doScanFile(path, mimeType, lastModifiedSeconds, file.length(),
141266dfcd465c02d78cb4ff22e7b509ee42d4309286Marco Nelissen                    false, true, MediaScanner.isNoMediaPath(path));
14139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (RemoteException e) {
14149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e);
14159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return null;
14161b37c3bddcdfe7cb60e776e3eb05bfbb46b0b282Mattias Nilsson        } finally {
14171b37c3bddcdfe7cb60e776e3eb05bfbb46b0b282Mattias Nilsson            releaseResources();
14189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
14209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1421ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood    private static boolean isNoMediaFile(String path) {
1422ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood        File file = new File(path);
1423ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood        if (file.isDirectory()) return false;
1424ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood
1425ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood        // special case certain file names
1426ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood        // I use regionMatches() instead of substring() below
1427ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood        // to avoid memory allocation
1428ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood        int lastSlash = path.lastIndexOf('/');
1429ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood        if (lastSlash >= 0 && lastSlash + 2 < path.length()) {
1430ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood            // ignore those ._* files created by MacOS
1431ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood            if (path.regionMatches(lastSlash + 1, "._", 0, 2)) {
1432ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood                return true;
1433ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood            }
1434ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood
1435ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood            // ignore album art files created by Windows Media Player:
1436ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood            // Folder.jpg, AlbumArtSmall.jpg, AlbumArt_{...}_Large.jpg
1437ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood            // and AlbumArt_{...}_Small.jpg
1438ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood            if (path.regionMatches(true, path.length() - 4, ".jpg", 0, 4)) {
1439ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood                if (path.regionMatches(true, lastSlash + 1, "AlbumArt_{", 0, 10) ||
1440ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood                        path.regionMatches(true, lastSlash + 1, "AlbumArt.", 0, 9)) {
1441ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood                    return true;
1442ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood                }
1443ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood                int length = path.length() - lastSlash - 1;
1444ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood                if ((length == 17 && path.regionMatches(
1445ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood                        true, lastSlash + 1, "AlbumArtSmall", 0, 13)) ||
1446ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood                        (length == 10
1447ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood                         && path.regionMatches(true, lastSlash + 1, "Folder", 0, 6))) {
1448ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood                    return true;
1449ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood                }
1450ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood            }
1451ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood        }
1452ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood        return false;
1453ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood    }
1454ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood
145593a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen    private static HashMap<String,String> mNoMediaPaths = new HashMap<String,String>();
145693a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen    private static HashMap<String,String> mMediaPaths = new HashMap<String,String>();
1457997354e4b4a9666cedd62282471e97822affced9Mike Lockwood
145893a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen    /* MediaProvider calls this when a .nomedia file is added or removed */
145993a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen    public static void clearMediaPathCache(boolean clearMediaPaths, boolean clearNoMediaPaths) {
146093a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen        synchronized (MediaScanner.class) {
146193a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen            if (clearMediaPaths) {
146293a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen                mMediaPaths.clear();
146393a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen            }
146493a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen            if (clearNoMediaPaths) {
146593a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen                mNoMediaPaths.clear();
146693a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen            }
146793a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen        }
146893a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen    }
146993a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen
147093a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen    public static boolean isNoMediaPath(String path) {
147193a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen        if (path == null) {
147293a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen            return false;
147393a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen        }
1474997354e4b4a9666cedd62282471e97822affced9Mike Lockwood        // return true if file or any parent directory has name starting with a dot
147593a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen        if (path.indexOf("/.") >= 0) {
147693a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen            return true;
147793a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen        }
147893a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen
147993a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen        int firstSlash = path.lastIndexOf('/');
1480d5a579765e06d69c1a33663c202c3593a5bd5c6bMarco Nelissen        if (firstSlash <= 0) {
148193a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen            return false;
148293a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen        }
148393a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen        String parent = path.substring(0,  firstSlash);
148493a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen
148593a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen        synchronized (MediaScanner.class) {
148693a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen            if (mNoMediaPaths.containsKey(parent)) {
148793a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen                return true;
148893a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen            } else if (!mMediaPaths.containsKey(parent)) {
148993a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen                // check to see if any parent directories have a ".nomedia" file
149093a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen                // start from 1 so we don't bother checking in the root directory
149193a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen                int offset = 1;
149293a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen                while (offset >= 0) {
149393a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen                    int slashIndex = path.indexOf('/', offset);
149493a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen                    if (slashIndex > offset) {
149593a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen                        slashIndex++; // move past slash
149693a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen                        File file = new File(path.substring(0, slashIndex) + ".nomedia");
149793a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen                        if (file.exists()) {
149893a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen                            // we have a .nomedia in one of the parent directories
149993a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen                            mNoMediaPaths.put(parent, "");
150093a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen                            return true;
150193a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen                        }
150293a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen                    }
150393a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen                    offset = slashIndex;
1504997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                }
150593a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen                mMediaPaths.put(parent, "");
1506997354e4b4a9666cedd62282471e97822affced9Mike Lockwood            }
1507997354e4b4a9666cedd62282471e97822affced9Mike Lockwood        }
150893a5845d1a56dde3be4f0bdae70044724548b7d1Marco Nelissen
1509ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood        return isNoMediaFile(path);
1510997354e4b4a9666cedd62282471e97822affced9Mike Lockwood    }
1511997354e4b4a9666cedd62282471e97822affced9Mike Lockwood
151260cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey    public void scanMtpFile(String path, int objectHandle, int format) {
1513c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood        MediaFile.MediaFileType mediaFileType = MediaFile.getFileType(path);
1514c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood        int fileType = (mediaFileType == null ? 0 : mediaFileType.fileType);
15157a0bd17bceaf3efc3732e30c538fae420d3b742cMike Lockwood        File file = new File(path);
15167a0bd17bceaf3efc3732e30c538fae420d3b742cMike Lockwood        long lastModifiedSeconds = file.lastModified() / 1000;
1517c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood
1518c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood        if (!MediaFile.isAudioFileType(fileType) && !MediaFile.isVideoFileType(fileType) &&
151920c38ea9fd4024cc076c825f46cdfb34ffc090deJan Bjernler            !MediaFile.isImageFileType(fileType) && !MediaFile.isPlayListFileType(fileType) &&
152020c38ea9fd4024cc076c825f46cdfb34ffc090deJan Bjernler            !MediaFile.isDrmFileType(fileType)) {
15217a0bd17bceaf3efc3732e30c538fae420d3b742cMike Lockwood
15227a0bd17bceaf3efc3732e30c538fae420d3b742cMike Lockwood            // no need to use the media scanner, but we need to update last modified and file size
15237a0bd17bceaf3efc3732e30c538fae420d3b742cMike Lockwood            ContentValues values = new ContentValues();
15247a0bd17bceaf3efc3732e30c538fae420d3b742cMike Lockwood            values.put(Files.FileColumns.SIZE, file.length());
15257a0bd17bceaf3efc3732e30c538fae420d3b742cMike Lockwood            values.put(Files.FileColumns.DATE_MODIFIED, lastModifiedSeconds);
15267a0bd17bceaf3efc3732e30c538fae420d3b742cMike Lockwood            try {
15277a0bd17bceaf3efc3732e30c538fae420d3b742cMike Lockwood                String[] whereArgs = new String[] {  Integer.toString(objectHandle) };
152860cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey                mMediaProvider.update(Files.getMtpObjectsUri(mVolumeName), values,
152935654b61e8fe7bc85afcb076ddbb590d51c5865fDianne Hackborn                        "_id=?", whereArgs);
15307a0bd17bceaf3efc3732e30c538fae420d3b742cMike Lockwood            } catch (RemoteException e) {
15317a0bd17bceaf3efc3732e30c538fae420d3b742cMike Lockwood                Log.e(TAG, "RemoteException in scanMtpFile", e);
15327a0bd17bceaf3efc3732e30c538fae420d3b742cMike Lockwood            }
1533c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood            return;
1534c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood        }
1535c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood
1536d815f79766984fce499e147ecbacc01914683f74Mike Lockwood        mMtpObjectHandle = objectHandle;
1537c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen        Cursor fileList = null;
1538c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood        try {
1539caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood            if (MediaFile.isPlayListFileType(fileType)) {
1540caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood                // build file cache so we can look up tracks in the playlist
1541caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood                prescan(null, true);
1542caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood
154358ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                FileEntry entry = makeEntryFor(path);
1544caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood                if (entry != null) {
154560cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey                    fileList = mMediaProvider.query(mFilesUri,
154635654b61e8fe7bc85afcb076ddbb590d51c5865fDianne Hackborn                            FILES_PRESCAN_PROJECTION, null, null, null, null);
1547c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen                    processPlayList(entry, fileList);
1548caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood                }
1549caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood            } else {
1550caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood                // MTP will create a file entry for us so we don't want to do it in prescan
1551caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood                prescan(path, false);
1552c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood
1553caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood                // always scan the file, so we can return the content://media Uri for existing files
1554076e05b488e40fdd946f0d35137fe66a576efe09Mike Lockwood                mClient.doScanFile(path, mediaFileType.mimeType, lastModifiedSeconds, file.length(),
1555997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                    (format == MtpConstants.FORMAT_ASSOCIATION), true, isNoMediaPath(path));
1556caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood            }
1557c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood        } catch (RemoteException e) {
1558c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood            Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e);
1559c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood        } finally {
1560c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood            mMtpObjectHandle = 0;
1561c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen            if (fileList != null) {
1562c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen                fileList.close();
1563c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen            }
15641b37c3bddcdfe7cb60e776e3eb05bfbb46b0b282Mattias Nilsson            releaseResources();
1565c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood        }
1566d815f79766984fce499e147ecbacc01914683f74Mike Lockwood    }
1567d815f79766984fce499e147ecbacc01914683f74Mike Lockwood
156858ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen    FileEntry makeEntryFor(String path) {
156958ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen        String where;
157058ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen        String[] selectionArgs;
157158ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen
157258ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen        Cursor c = null;
157358ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen        try {
1574da8fe0faf5b52ef65bd07b6f47479ac537d42d47Marco Nelissen            where = Files.FileColumns.DATA + "=?";
1575da8fe0faf5b52ef65bd07b6f47479ac537d42d47Marco Nelissen            selectionArgs = new String[] { path };
157660cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey            c = mMediaProvider.query(mFilesUriNoNotify, FILES_PRESCAN_PROJECTION,
157758ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                    where, selectionArgs, null, null);
15788ab2dc2f9680307febe997631c2148729f714e3dMarco Nelissen            if (c.moveToFirst()) {
157958ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX);
158058ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                int format = c.getInt(FILES_PRESCAN_FORMAT_COLUMN_INDEX);
158158ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                long lastModified = c.getLong(FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
158258ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                return new FileEntry(rowId, path, lastModified, format);
158358ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen            }
158458ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen        } catch (RemoteException e) {
158558ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen        } finally {
158658ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen            if (c != null) {
158758ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                c.close();
158858ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen            }
158958ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen        }
159058ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen        return null;
159158ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen    }
159258ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen
15939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // returns the number of matching file/directory names, starting from the right
15949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int matchPaths(String path1, String path2) {
15959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int result = 0;
15969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int end1 = path1.length();
15979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int end2 = path2.length();
159820b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
15999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        while (end1 > 0 && end2 > 0) {
16009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int slash1 = path1.lastIndexOf('/', end1 - 1);
16019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int slash2 = path2.lastIndexOf('/', end2 - 1);
16029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int backSlash1 = path1.lastIndexOf('\\', end1 - 1);
16039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int backSlash2 = path2.lastIndexOf('\\', end2 - 1);
16049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int start1 = (slash1 > backSlash1 ? slash1 : backSlash1);
16059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int start2 = (slash2 > backSlash2 ? slash2 : backSlash2);
16069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (start1 < 0) start1 = 0; else start1++;
16079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (start2 < 0) start2 = 0; else start2++;
16089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int length = end1 - start1;
16099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (end2 - start2 != length) break;
16109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (path1.regionMatches(true, start1, path2, start2, length)) {
16119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                result++;
16129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                end1 = start1 - 1;
16139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                end2 = start2 - 1;
16149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else break;
16159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
161620b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
16179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return result;
16189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
16199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16200b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen    private boolean matchEntries(long rowId, String data) {
16210b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen
16220b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen        int len = mPlaylistEntries.size();
16230b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen        boolean done = true;
16240b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen        for (int i = 0; i < len; i++) {
16250b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen            PlaylistEntry entry = mPlaylistEntries.get(i);
16260b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen            if (entry.bestmatchlevel == Integer.MAX_VALUE) {
16270b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen                continue; // this entry has been matched already
16280b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen            }
16290b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen            done = false;
16300b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen            if (data.equalsIgnoreCase(entry.path)) {
16310b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen                entry.bestmatchid = rowId;
16320b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen                entry.bestmatchlevel = Integer.MAX_VALUE;
16330b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen                continue; // no need for path matching
16340b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen            }
16350b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen
16360b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen            int matchLength = matchPaths(data, entry.path);
16370b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen            if (matchLength > entry.bestmatchlevel) {
16380b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen                entry.bestmatchid = rowId;
16390b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen                entry.bestmatchlevel = matchLength;
16400b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen            }
16410b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen        }
16420b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen        return done;
16430b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen    }
164420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
16450b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen    private void cachePlaylistEntry(String line, String playListDirectory) {
16460b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen        PlaylistEntry entry = new PlaylistEntry();
16479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // watch for trailing whitespace
16480b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen        int entryLength = line.length();
16490b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen        while (entryLength > 0 && Character.isWhitespace(line.charAt(entryLength - 1))) entryLength--;
16509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // path should be longer than 3 characters.
16519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // avoid index out of bounds errors below by returning here.
16520b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen        if (entryLength < 3) return;
16530b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen        if (entryLength < line.length()) line = line.substring(0, entryLength);
16549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // does entry appear to be an absolute path?
16569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // look for Unix or DOS absolute paths
16570b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen        char ch1 = line.charAt(0);
16589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean fullPath = (ch1 == '/' ||
16590b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen                (Character.isLetter(ch1) && line.charAt(1) == ':' && line.charAt(2) == '\\'));
16609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // if we have a relative path, combine entry with playListDirectory
16619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!fullPath)
16620b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen            line = playListDirectory + line;
16630b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen        entry.path = line;
16649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        //FIXME - should we look for "../" within the path?
166520b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
16660b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen        mPlaylistEntries.add(entry);
16670b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen    }
166820b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
16690b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen    private void processCachedPlaylist(Cursor fileList, ContentValues values, Uri playlistUri) {
16700b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen        fileList.moveToPosition(-1);
16710b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen        while (fileList.moveToNext()) {
16720b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen            long rowId = fileList.getLong(FILES_PRESCAN_ID_COLUMN_INDEX);
16730b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen            String data = fileList.getString(FILES_PRESCAN_PATH_COLUMN_INDEX);
16740b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen            if (matchEntries(rowId, data)) {
16750b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen                break;
16769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
16779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
167820b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
16790b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen        int len = mPlaylistEntries.size();
16800b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen        int index = 0;
16810b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen        for (int i = 0; i < len; i++) {
16820b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen            PlaylistEntry entry = mPlaylistEntries.get(i);
16830b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen            if (entry.bestmatchlevel > 0) {
16840b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen                try {
16850b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen                    values.clear();
16860b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen                    values.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER, Integer.valueOf(index));
16870b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen                    values.put(MediaStore.Audio.Playlists.Members.AUDIO_ID, Long.valueOf(entry.bestmatchid));
168860cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey                    mMediaProvider.insert(playlistUri, values);
16890b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen                    index++;
16900b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen                } catch (RemoteException e) {
16910b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen                    Log.e(TAG, "RemoteException in MediaScanner.processCachedPlaylist()", e);
16920b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen                    return;
16937d92a22d9dec42a45cd55fd77d0a86b4b9c6e9dfDongwon Kang                }
16947d92a22d9dec42a45cd55fd77d0a86b4b9c6e9dfDongwon Kang            }
16959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
16960b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen        mPlaylistEntries.clear();
16979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
169820b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
1699c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen    private void processM3uPlayList(String path, String playListDirectory, Uri uri,
1700c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen            ContentValues values, Cursor fileList) {
17019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        BufferedReader reader = null;
17029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
17039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            File f = new File(path);
17049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (f.exists()) {
17059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                reader = new BufferedReader(
17069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        new InputStreamReader(new FileInputStream(f)), 8192);
17079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                String line = reader.readLine();
17080b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen                mPlaylistEntries.clear();
17099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                while (line != null) {
17109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // ignore comment lines, which begin with '#'
17119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (line.length() > 0 && line.charAt(0) != '#') {
17120b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen                        cachePlaylistEntry(line, playListDirectory);
17139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
17149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    line = reader.readLine();
17159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
17160b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen
17170b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen                processCachedPlaylist(fileList, values, uri);
17189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
17199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IOException e) {
17209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.e(TAG, "IOException in MediaScanner.processM3uPlayList()", e);
17219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } finally {
17229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
17239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (reader != null)
17249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    reader.close();
17259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (IOException e) {
17269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Log.e(TAG, "IOException in MediaScanner.processM3uPlayList()", e);
17279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
17289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
17299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
17309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1731c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen    private void processPlsPlayList(String path, String playListDirectory, Uri uri,
1732c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen            ContentValues values, Cursor fileList) {
17339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        BufferedReader reader = null;
17349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
17359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            File f = new File(path);
17369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (f.exists()) {
17379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                reader = new BufferedReader(
17389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        new InputStreamReader(new FileInputStream(f)), 8192);
17399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                String line = reader.readLine();
17400b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen                mPlaylistEntries.clear();
17419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                while (line != null) {
17429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // ignore comment lines, which begin with '#'
17439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (line.startsWith("File")) {
17449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        int equals = line.indexOf('=');
17459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (equals > 0) {
1746538d4925b36067dc89ad0625a69dafeaa364bd2eMarco Nelissen                            cachePlaylistEntry(line.substring(equals + 1), playListDirectory);
17479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
17489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
17499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    line = reader.readLine();
17509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
17510b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen
17520b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen                processCachedPlaylist(fileList, values, uri);
17539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
17549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IOException e) {
17559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.e(TAG, "IOException in MediaScanner.processPlsPlayList()", e);
17569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } finally {
17579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
17589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (reader != null)
17599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    reader.close();
17609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (IOException e) {
17619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Log.e(TAG, "IOException in MediaScanner.processPlsPlayList()", e);
17629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
17639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
17649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
17659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
17669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    class WplHandler implements ElementListener {
17679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
17689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final ContentHandler handler;
17699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String playListDirectory;
17709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1771c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen        public WplHandler(String playListDirectory, Uri uri, Cursor fileList) {
17729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.playListDirectory = playListDirectory;
177320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
17749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            RootElement root = new RootElement("smil");
17759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Element body = root.getChild("body");
17769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Element seq = body.getChild("seq");
17779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Element media = seq.getChild("media");
17789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            media.setElementListener(this);
17799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
17809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.handler = root.getContentHandler();
17819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
17829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1783c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen        @Override
17849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void start(Attributes attributes) {
17859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String path = attributes.getValue("", "src");
17869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (path != null) {
17870b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen                cachePlaylistEntry(path, playListDirectory);
17889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
17899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
17909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
17910b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen       @Override
17929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project       public void end() {
17939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project       }
17949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
17959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        ContentHandler getContentHandler() {
17969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return handler;
17979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
17989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
17999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1800c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen    private void processWplPlayList(String path, String playListDirectory, Uri uri,
18010b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen            ContentValues values, Cursor fileList) {
18029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        FileInputStream fis = null;
18039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
18049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            File f = new File(path);
18059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (f.exists()) {
18069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                fis = new FileInputStream(f);
18079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
18080b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen                mPlaylistEntries.clear();
1809c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen                Xml.parse(fis, Xml.findEncodingByName("UTF-8"),
1810c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen                        new WplHandler(playListDirectory, uri, fileList).getContentHandler());
18110b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen
18120b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen                processCachedPlaylist(fileList, values, uri);
18139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
18149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (SAXException e) {
18159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            e.printStackTrace();
18169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IOException e) {
18179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            e.printStackTrace();
18189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } finally {
18199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
18209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (fis != null)
18219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    fis.close();
18229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (IOException e) {
18239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Log.e(TAG, "IOException in MediaScanner.processWplPlayList()", e);
18249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
18259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
18269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
182720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
1828c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen    private void processPlayList(FileEntry entry, Cursor fileList) throws RemoteException {
1829caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood        String path = entry.mPath;
1830caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood        ContentValues values = new ContentValues();
1831caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood        int lastSlash = path.lastIndexOf('/');
1832caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood        if (lastSlash < 0) throw new IllegalArgumentException("bad path " + path);
1833caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood        Uri uri, membersUri;
1834caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood        long rowId = entry.mRowId;
1835caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood
1836242d0cdc1ad6c48faca403ea352bde73138443b5Mike Lockwood        // make sure we have a name
1837242d0cdc1ad6c48faca403ea352bde73138443b5Mike Lockwood        String name = values.getAsString(MediaStore.Audio.Playlists.NAME);
1838242d0cdc1ad6c48faca403ea352bde73138443b5Mike Lockwood        if (name == null) {
1839242d0cdc1ad6c48faca403ea352bde73138443b5Mike Lockwood            name = values.getAsString(MediaStore.MediaColumns.TITLE);
1840242d0cdc1ad6c48faca403ea352bde73138443b5Mike Lockwood            if (name == null) {
1841242d0cdc1ad6c48faca403ea352bde73138443b5Mike Lockwood                // extract name from file name
1842242d0cdc1ad6c48faca403ea352bde73138443b5Mike Lockwood                int lastDot = path.lastIndexOf('.');
1843242d0cdc1ad6c48faca403ea352bde73138443b5Mike Lockwood                name = (lastDot < 0 ? path.substring(lastSlash + 1)
1844242d0cdc1ad6c48faca403ea352bde73138443b5Mike Lockwood                        : path.substring(lastSlash + 1, lastDot));
1845242d0cdc1ad6c48faca403ea352bde73138443b5Mike Lockwood            }
1846242d0cdc1ad6c48faca403ea352bde73138443b5Mike Lockwood        }
1847242d0cdc1ad6c48faca403ea352bde73138443b5Mike Lockwood
1848242d0cdc1ad6c48faca403ea352bde73138443b5Mike Lockwood        values.put(MediaStore.Audio.Playlists.NAME, name);
1849242d0cdc1ad6c48faca403ea352bde73138443b5Mike Lockwood        values.put(MediaStore.Audio.Playlists.DATE_MODIFIED, entry.mLastModified);
1850242d0cdc1ad6c48faca403ea352bde73138443b5Mike Lockwood
1851242d0cdc1ad6c48faca403ea352bde73138443b5Mike Lockwood        if (rowId == 0) {
1852caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood            values.put(MediaStore.Audio.Playlists.DATA, path);
185360cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey            uri = mMediaProvider.insert(mPlaylistsUri, values);
1854caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood            rowId = ContentUris.parseId(uri);
1855caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood            membersUri = Uri.withAppendedPath(uri, Playlists.Members.CONTENT_DIRECTORY);
1856caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood        } else {
1857caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood            uri = ContentUris.withAppendedId(mPlaylistsUri, rowId);
185860cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey            mMediaProvider.update(uri, values, null, null);
185920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
1860caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood            // delete members of existing playlist
1861caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood            membersUri = Uri.withAppendedPath(uri, Playlists.Members.CONTENT_DIRECTORY);
186260cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey            mMediaProvider.delete(membersUri, null, null);
1863caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood        }
18649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1865caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood        String playListDirectory = path.substring(0, lastSlash + 1);
1866caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood        MediaFile.MediaFileType mediaFileType = MediaFile.getFileType(path);
1867caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood        int fileType = (mediaFileType == null ? 0 : mediaFileType.fileType);
186820b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
1869caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood        if (fileType == MediaFile.FILE_TYPE_M3U) {
1870c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen            processM3uPlayList(path, playListDirectory, membersUri, values, fileList);
1871caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood        } else if (fileType == MediaFile.FILE_TYPE_PLS) {
1872c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen            processPlsPlayList(path, playListDirectory, membersUri, values, fileList);
1873caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood        } else if (fileType == MediaFile.FILE_TYPE_WPL) {
18740b718392c1f50f45760b198a5d9e8df2cf2b5080Marco Nelissen            processWplPlayList(path, playListDirectory, membersUri, values, fileList);
1875caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood        }
1876caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood    }
187720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
1878caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood    private void processPlayLists() throws RemoteException {
187958ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen        Iterator<FileEntry> iterator = mPlayLists.iterator();
1880c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen        Cursor fileList = null;
1881c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen        try {
188208f70fa5b82faa00d4d536b5a220d7ce8dd8f932Marco Nelissen            // use the files uri and projection because we need the format column,
188308f70fa5b82faa00d4d536b5a220d7ce8dd8f932Marco Nelissen            // but restrict the query to just audio files
188460cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey            fileList = mMediaProvider.query(mFilesUri, FILES_PRESCAN_PROJECTION,
188508f70fa5b82faa00d4d536b5a220d7ce8dd8f932Marco Nelissen                    "media_type=2", null, null, null);
1886c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen            while (iterator.hasNext()) {
1887c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen                FileEntry entry = iterator.next();
1888c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen                // only process playlist files if they are new or have been modified since the last scan
1889c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen                if (entry.mLastModifiedChanged) {
1890c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen                    processPlayList(entry, fileList);
1891c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen                }
1892c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen            }
1893c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen        } catch (RemoteException e1) {
1894c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen        } finally {
1895c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen            if (fileList != null) {
1896c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen                fileList.close();
18979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
18989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
18999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
190020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
1901c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood    private native void processDirectory(String path, MediaScannerClient client);
1902bab75909fac9c65b71012f4c06302503c2f72be1Marco Nelissen    private native boolean processFile(String path, String mimeType, MediaScannerClient client);
190360cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey    private native void setLocale(String locale);
190420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
19059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public native byte[] extractAlbumArt(FileDescriptor fd);
19069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
19074935d05eaa306cef88cf0ab13eca386f270409ecMarco Nelissen    private static native final void native_init();
19089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private native final void native_setup();
19099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private native final void native_finalize();
1910133cf8b92a07d5be8c64607b9644aa9cef169fb0James Dong
191160cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey    @Override
191260cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey    public void close() {
191360cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey        mCloseGuard.close();
191460cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey        if (mClosed.compareAndSet(false, true)) {
191560cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey            mMediaProvider.close();
191660cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey            native_finalize();
191760cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey        }
1918133cf8b92a07d5be8c64607b9644aa9cef169fb0James Dong    }
1919133cf8b92a07d5be8c64607b9644aa9cef169fb0James Dong
19209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
192160cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey    protected void finalize() throws Throwable {
192260cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey        try {
1923492e9e851cadca62df84eaff1a3c1ba788492fbaNarayan Kamath            if (mCloseGuard != null) {
1924492e9e851cadca62df84eaff1a3c1ba788492fbaNarayan Kamath                mCloseGuard.warnIfOpen();
1925492e9e851cadca62df84eaff1a3c1ba788492fbaNarayan Kamath            }
1926492e9e851cadca62df84eaff1a3c1ba788492fbaNarayan Kamath
192760cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey            close();
192860cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey        } finally {
192960cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey            super.finalize();
193060cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey        }
19319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
19329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
1933