summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--media/java/android/media/MediaScanner.java1960
-rw-r--r--media/java/android/media/RingtoneManager.java2
-rw-r--r--media/jni/Android.bp1
-rw-r--r--media/jni/android_media_MediaPlayer.cpp6
-rw-r--r--media/jni/android_media_MediaScanner.cpp468
5 files changed, 88 insertions, 2349 deletions
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 771628cf7b1e..ca96c9ab5670 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -17,106 +17,14 @@
package android.media;
import android.annotation.UnsupportedAppUsage;
-import android.content.ContentProviderClient;
-import android.content.ContentResolver;
-import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
-import android.content.SharedPreferences;
-import android.database.Cursor;
-import android.database.SQLException;
-import android.drm.DrmManagerClient;
-import android.graphics.BitmapFactory;
-import android.mtp.MtpConstants;
import android.net.Uri;
import android.os.Build;
-import android.os.Environment;
import android.os.RemoteException;
-import android.os.SystemProperties;
-import android.provider.MediaStore;
-import android.provider.MediaStore.Audio;
-import android.provider.MediaStore.Audio.Playlists;
-import android.provider.MediaStore.Files;
-import android.provider.MediaStore.Files.FileColumns;
-import android.provider.MediaStore.Images;
-import android.provider.MediaStore.Video;
-import android.provider.Settings;
-import android.provider.Settings.SettingNotFoundException;
-import android.sax.Element;
-import android.sax.ElementListener;
-import android.sax.RootElement;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.Xml;
-
-import dalvik.system.CloseGuard;
-
-import org.xml.sax.Attributes;
-import org.xml.sax.ContentHandler;
-import org.xml.sax.SAXException;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Locale;
-import java.util.TimeZone;
-import java.util.concurrent.atomic.AtomicBoolean;
/**
- * Internal service helper that no-one should use directly.
- *
- * The way the scan currently works is:
- * - The Java MediaScannerService creates a MediaScanner (this class), and calls
- * MediaScanner.scanDirectories on it.
- * - scanDirectories() calls the native processDirectory() for each of the specified directories.
- * - the processDirectory() JNI method wraps the provided mediascanner client in a native
- * 'MyMediaScannerClient' class, then calls processDirectory() on the native MediaScanner
- * object (which got created when the Java MediaScanner was created).
- * - native MediaScanner.processDirectory() calls
- * doProcessDirectory(), which recurses over the folder, and calls
- * native MyMediaScannerClient.scanFile() for every file whose extension matches.
- * - native MyMediaScannerClient.scanFile() calls back on Java MediaScannerClient.scanFile,
- * which calls doScanFile, which after some setup calls back down to native code, calling
- * MediaScanner.processFile().
- * - MediaScanner.processFile() calls one of several methods, depending on the type of the
- * file: parseMP3, parseMP4, parseMidi, parseOgg or parseWMA.
- * - each of these methods gets metadata key/value pairs from the file, and repeatedly
- * calls native MyMediaScannerClient.handleStringTag, which calls back up to its Java
- * counterparts in this file.
- * - Java handleStringTag() gathers the key/value pairs that it's interested in.
- * - once processFile returns and we're back in Java code in doScanFile(), it calls
- * Java MyMediaScannerClient.endFile(), which takes all the data that's been
- * gathered and inserts an entry in to the database.
- *
- * In summary:
- * Java MediaScannerService calls
- * Java MediaScanner scanDirectories, which calls
- * Java MediaScanner processDirectory (native method), which calls
- * native MediaScanner processDirectory, which calls
- * native MyMediaScannerClient scanFile, which calls
- * Java MyMediaScannerClient scanFile, which calls
- * Java MediaScannerClient doScanFile, which calls
- * Java MediaScanner processFile (native method), which calls
- * native MediaScanner processFile, which calls
- * native parseMP3, parseMP4, parseMidi, parseOgg or parseWMA, which calls
- * native MyMediaScanner handleStringTag, which calls
- * Java MyMediaScanner handleStringTag.
- * Once MediaScanner processFile returns, an entry is inserted in to the database.
- *
- * The MediaScanner class is not thread-safe, so it should only be used in a single threaded manner.
- *
- * {@hide}
- *
+ * @hide
* @deprecated this media scanner has served faithfully for many years, but it's
* become tedious to test and maintain, mainly due to the way it
* weaves obscurely between managed and native code. It's been
@@ -125,1876 +33,182 @@ import java.util.concurrent.atomic.AtomicBoolean;
*/
@Deprecated
public class MediaScanner implements AutoCloseable {
- static {
- System.loadLibrary("media_jni");
- native_init();
- }
-
- private final static String TAG = "MediaScanner";
-
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private static final String[] FILES_PRESCAN_PROJECTION = new String[] {
- Files.FileColumns._ID, // 0
- Files.FileColumns.DATA, // 1
- Files.FileColumns.FORMAT, // 2
- Files.FileColumns.DATE_MODIFIED, // 3
- Files.FileColumns.MEDIA_TYPE, // 4
- };
-
- private static final String[] ID_PROJECTION = new String[] {
- Files.FileColumns._ID,
- };
-
- private static final int FILES_PRESCAN_ID_COLUMN_INDEX = 0;
- private static final int FILES_PRESCAN_PATH_COLUMN_INDEX = 1;
- private static final int FILES_PRESCAN_FORMAT_COLUMN_INDEX = 2;
- private static final int FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX = 3;
- private static final int FILES_PRESCAN_MEDIA_TYPE_COLUMN_INDEX = 4;
-
- private static final String[] PLAYLIST_MEMBERS_PROJECTION = new String[] {
- Audio.Playlists.Members.PLAYLIST_ID, // 0
- };
-
- private static final int ID_PLAYLISTS_COLUMN_INDEX = 0;
- private static final int PATH_PLAYLISTS_COLUMN_INDEX = 1;
- private static final int DATE_MODIFIED_PLAYLISTS_COLUMN_INDEX = 2;
-
- private static final String RINGTONES_DIR = "/ringtones/";
- private static final String NOTIFICATIONS_DIR = "/notifications/";
- private static final String ALARMS_DIR = "/alarms/";
- private static final String MUSIC_DIR = "/music/";
- private static final String PODCASTS_DIR = "/podcasts/";
- private static final String AUDIOBOOKS_DIR = "/audiobooks/";
-
- public static final String SCANNED_BUILD_PREFS_NAME = "MediaScanBuild";
- public static final String LAST_INTERNAL_SCAN_FINGERPRINT = "lastScanFingerprint";
- private static final String SYSTEM_SOUNDS_DIR = Environment.getRootDirectory() + "/media/audio";
- private static final String OEM_SOUNDS_DIR = Environment.getOemDirectory() + "/media/audio";
- private static final String PRODUCT_SOUNDS_DIR = Environment.getProductDirectory() + "/media/audio";
- private static String sLastInternalScanFingerprint;
-
- private static final String[] ID3_GENRES = {
- // ID3v1 Genres
- "Blues",
- "Classic Rock",
- "Country",
- "Dance",
- "Disco",
- "Funk",
- "Grunge",
- "Hip-Hop",
- "Jazz",
- "Metal",
- "New Age",
- "Oldies",
- "Other",
- "Pop",
- "R&B",
- "Rap",
- "Reggae",
- "Rock",
- "Techno",
- "Industrial",
- "Alternative",
- "Ska",
- "Death Metal",
- "Pranks",
- "Soundtrack",
- "Euro-Techno",
- "Ambient",
- "Trip-Hop",
- "Vocal",
- "Jazz+Funk",
- "Fusion",
- "Trance",
- "Classical",
- "Instrumental",
- "Acid",
- "House",
- "Game",
- "Sound Clip",
- "Gospel",
- "Noise",
- "AlternRock",
- "Bass",
- "Soul",
- "Punk",
- "Space",
- "Meditative",
- "Instrumental Pop",
- "Instrumental Rock",
- "Ethnic",
- "Gothic",
- "Darkwave",
- "Techno-Industrial",
- "Electronic",
- "Pop-Folk",
- "Eurodance",
- "Dream",
- "Southern Rock",
- "Comedy",
- "Cult",
- "Gangsta",
- "Top 40",
- "Christian Rap",
- "Pop/Funk",
- "Jungle",
- "Native American",
- "Cabaret",
- "New Wave",
- "Psychadelic",
- "Rave",
- "Showtunes",
- "Trailer",
- "Lo-Fi",
- "Tribal",
- "Acid Punk",
- "Acid Jazz",
- "Polka",
- "Retro",
- "Musical",
- "Rock & Roll",
- "Hard Rock",
- // The following genres are Winamp extensions
- "Folk",
- "Folk-Rock",
- "National Folk",
- "Swing",
- "Fast Fusion",
- "Bebob",
- "Latin",
- "Revival",
- "Celtic",
- "Bluegrass",
- "Avantgarde",
- "Gothic Rock",
- "Progressive Rock",
- "Psychedelic Rock",
- "Symphonic Rock",
- "Slow Rock",
- "Big Band",
- "Chorus",
- "Easy Listening",
- "Acoustic",
- "Humour",
- "Speech",
- "Chanson",
- "Opera",
- "Chamber Music",
- "Sonata",
- "Symphony",
- "Booty Bass",
- "Primus",
- "Porn Groove",
- "Satire",
- "Slow Jam",
- "Club",
- "Tango",
- "Samba",
- "Folklore",
- "Ballad",
- "Power Ballad",
- "Rhythmic Soul",
- "Freestyle",
- "Duet",
- "Punk Rock",
- "Drum Solo",
- "A capella",
- "Euro-House",
- "Dance Hall",
- // The following ones seem to be fairly widely supported as well
- "Goa",
- "Drum & Bass",
- "Club-House",
- "Hardcore",
- "Terror",
- "Indie",
- "Britpop",
- null,
- "Polsk Punk",
- "Beat",
- "Christian Gangsta",
- "Heavy Metal",
- "Black Metal",
- "Crossover",
- "Contemporary Christian",
- "Christian Rock",
- "Merengue",
- "Salsa",
- "Thrash Metal",
- "Anime",
- "JPop",
- "Synthpop",
- // 148 and up don't seem to have been defined yet.
};
- private long mNativeContext;
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private final Context mContext;
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private final String mPackageName;
- private final String mVolumeName;
- private final ContentProviderClient mMediaProvider;
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private final Uri mAudioUri;
- private final Uri mVideoUri;
- private final Uri mImagesUri;
- private final Uri mPlaylistsUri;
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private final Uri mFilesUri;
- private final Uri mFilesFullUri;
- private final boolean mProcessPlaylists;
- private final boolean mProcessGenres;
- private int mMtpObjectHandle;
- private final AtomicBoolean mClosed = new AtomicBoolean();
- private final CloseGuard mCloseGuard = CloseGuard.get();
-
- /** whether to use bulk inserts or individual inserts for each item */
- private static final boolean ENABLE_BULK_INSERTS = true;
-
- // used when scanning the image database so we know whether we have to prune
- // old thumbnail files
- private int mOriginalCount;
- /** Whether the scanner has set a default sound for the ringer ringtone. */
- private boolean mDefaultRingtoneSet;
- /** Whether the scanner has set a default sound for the notification ringtone. */
- private boolean mDefaultNotificationSet;
- /** Whether the scanner has set a default sound for the alarm ringtone. */
- private boolean mDefaultAlarmSet;
- /** The filename for the default sound for the ringer ringtone. */
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private String mDefaultRingtoneFilename;
- /** The filename for the default sound for the notification ringtone. */
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private String mDefaultNotificationFilename;
- /** The filename for the default sound for the alarm ringtone. */
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private String mDefaultAlarmAlertFilename;
- /**
- * The prefix for system properties that define the default sound for
- * ringtones. Concatenate the name of the setting from Settings
- * to get the full system property.
- */
- private static final String DEFAULT_RINGTONE_PROPERTY_PREFIX = "ro.config.";
-
- private final BitmapFactory.Options mBitmapOptions = new BitmapFactory.Options();
private static class FileEntry {
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
long mRowId;
- String mPath;
- long mLastModified;
- int mFormat;
- int mMediaType;
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
boolean mLastModifiedChanged;
- /** @deprecated kept intact for lame apps using reflection */
@Deprecated
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
FileEntry(long rowId, String path, long lastModified, int format) {
- this(rowId, path, lastModified, format, FileColumns.MEDIA_TYPE_NONE);
- }
-
- FileEntry(long rowId, String path, long lastModified, int format, int mediaType) {
- mRowId = rowId;
- mPath = path;
- mLastModified = lastModified;
- mFormat = format;
- mMediaType = mediaType;
- mLastModifiedChanged = false;
- }
-
- @Override
- public String toString() {
- return mPath + " mRowId: " + mRowId;
+ throw new UnsupportedOperationException();
}
}
- private static class PlaylistEntry {
- String path;
- long bestmatchid;
- int bestmatchlevel;
- }
-
- private final ArrayList<PlaylistEntry> mPlaylistEntries = new ArrayList<>();
- private final ArrayList<FileEntry> mPlayLists = new ArrayList<>();
-
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private MediaInserter mMediaInserter;
- private DrmManagerClient mDrmManagerClient = null;
-
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
public MediaScanner(Context c, String volumeName) {
- native_setup();
- mContext = c;
- mPackageName = c.getPackageName();
- mVolumeName = volumeName;
-
- mBitmapOptions.inSampleSize = 1;
- mBitmapOptions.inJustDecodeBounds = true;
-
- setDefaultRingtoneFileNames();
-
- mMediaProvider = mContext.getContentResolver()
- .acquireContentProviderClient(MediaStore.AUTHORITY);
-
- if (sLastInternalScanFingerprint == null) {
- final SharedPreferences scanSettings =
- mContext.getSharedPreferences(SCANNED_BUILD_PREFS_NAME, Context.MODE_PRIVATE);
- sLastInternalScanFingerprint =
- scanSettings.getString(LAST_INTERNAL_SCAN_FINGERPRINT, new String());
- }
-
- mAudioUri = Audio.Media.getContentUri(volumeName);
- mVideoUri = Video.Media.getContentUri(volumeName);
- mImagesUri = Images.Media.getContentUri(volumeName);
- mFilesUri = Files.getContentUri(volumeName);
-
- Uri filesFullUri = mFilesUri.buildUpon().appendQueryParameter("nonotify", "1").build();
- filesFullUri = MediaStore.setIncludePending(filesFullUri);
- filesFullUri = MediaStore.setIncludeTrashed(filesFullUri);
- mFilesFullUri = filesFullUri;
-
- if (!volumeName.equals("internal")) {
- // we only support playlists on external media
- mProcessPlaylists = true;
- mProcessGenres = true;
- mPlaylistsUri = Playlists.getContentUri(volumeName);
- } else {
- mProcessPlaylists = false;
- mProcessGenres = false;
- mPlaylistsUri = null;
- }
-
- final Locale locale = mContext.getResources().getConfiguration().locale;
- if (locale != null) {
- String language = locale.getLanguage();
- String country = locale.getCountry();
- if (language != null) {
- if (country != null) {
- setLocale(language + "_" + country);
- } else {
- setLocale(language);
- }
- }
- }
-
- mCloseGuard.open("close");
- }
-
- private void setDefaultRingtoneFileNames() {
- mDefaultRingtoneFilename = SystemProperties.get(DEFAULT_RINGTONE_PROPERTY_PREFIX
- + Settings.System.RINGTONE);
- mDefaultNotificationFilename = SystemProperties.get(DEFAULT_RINGTONE_PROPERTY_PREFIX
- + Settings.System.NOTIFICATION_SOUND);
- mDefaultAlarmAlertFilename = SystemProperties.get(DEFAULT_RINGTONE_PROPERTY_PREFIX
- + Settings.System.ALARM_ALERT);
+ throw new UnsupportedOperationException();
}
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private final MyMediaScannerClient mClient = new MyMediaScannerClient();
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private boolean isDrmEnabled() {
- String prop = SystemProperties.get("drm.service.enabled");
- return prop != null && prop.equals("true");
+ throw new UnsupportedOperationException();
}
private class MyMediaScannerClient implements MediaScannerClient {
-
- private final SimpleDateFormat mDateFormatter;
-
- private String mArtist;
- private String mAlbumArtist; // use this if mArtist is missing
- private String mAlbum;
- private String mTitle;
- private String mComposer;
- private String mGenre;
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private String mMimeType;
- /** @deprecated file types no longer exist */
@Deprecated
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private int mFileType;
- private int mTrack;
- private int mYear;
- private int mDuration;
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private String mPath;
- private long mDate;
- private long mLastModified;
- private long mFileSize;
- private String mWriter;
- private int mCompilation;
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private boolean mIsDrm;
- @UnsupportedAppUsage
- private boolean mNoMedia; // flag to suppress file from appearing in media tables
- private boolean mScanSuccess;
- private int mWidth;
- private int mHeight;
- private int mColorStandard;
- private int mColorTransfer;
- private int mColorRange;
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
+ private boolean mNoMedia;
public MyMediaScannerClient() {
- mDateFormatter = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
- mDateFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
+ throw new UnsupportedOperationException();
}
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
public FileEntry beginFile(String path, String mimeType, long lastModified,
long fileSize, boolean isDirectory, boolean noMedia) {
- mMimeType = mimeType;
- mFileSize = fileSize;
- mIsDrm = false;
- mScanSuccess = true;
-
- if (!isDirectory) {
- if (!noMedia && isNoMediaFile(path)) {
- noMedia = true;
- }
- mNoMedia = noMedia;
-
- // if mimeType was not specified, compute file type based on file extension.
- if (mMimeType == null) {
- mMimeType = MediaFile.getMimeTypeForFile(path);
- }
-
- if (isDrmEnabled() && MediaFile.isDrmMimeType(mMimeType)) {
- getMimeTypeFromDrm(path);
- }
- }
-
- FileEntry entry = makeEntryFor(path);
- // add some slack to avoid a rounding error
- long delta = (entry != null) ? (lastModified - entry.mLastModified) : 0;
- boolean wasModified = delta > 1 || delta < -1;
- if (entry == null || wasModified) {
- if (wasModified) {
- entry.mLastModified = lastModified;
- } else {
- entry = new FileEntry(0, path, lastModified,
- (isDirectory ? MtpConstants.FORMAT_ASSOCIATION : 0),
- FileColumns.MEDIA_TYPE_NONE);
- }
- entry.mLastModifiedChanged = true;
- }
-
- if (mProcessPlaylists && MediaFile.isPlayListMimeType(mMimeType)) {
- mPlayLists.add(entry);
- // we don't process playlists in the main scan, so return null
- return null;
- }
-
- // clear all the metadata
- mArtist = null;
- mAlbumArtist = null;
- mAlbum = null;
- mTitle = null;
- mComposer = null;
- mGenre = null;
- mTrack = 0;
- mYear = 0;
- mDuration = 0;
- mPath = path;
- mDate = 0;
- mLastModified = lastModified;
- mWriter = null;
- mCompilation = 0;
- mWidth = 0;
- mHeight = 0;
- mColorStandard = -1;
- mColorTransfer = -1;
- mColorRange = -1;
-
- return entry;
+ throw new UnsupportedOperationException();
}
- @Override
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
public void scanFile(String path, long lastModified, long fileSize,
boolean isDirectory, boolean noMedia) {
- // This is the callback funtion from native codes.
- // Log.v(TAG, "scanFile: "+path);
- doScanFile(path, null, lastModified, fileSize, isDirectory, false, noMedia);
+ throw new UnsupportedOperationException();
}
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
public Uri doScanFile(String path, String mimeType, long lastModified,
long fileSize, boolean isDirectory, boolean scanAlways, boolean noMedia) {
- Uri result = null;
-// long t1 = System.currentTimeMillis();
- try {
- FileEntry entry = beginFile(path, mimeType, lastModified,
- fileSize, isDirectory, noMedia);
-
- if (entry == null) {
- return null;
- }
-
- // if this file was just inserted via mtp, set the rowid to zero
- // (even though it already exists in the database), to trigger
- // the correct code path for updating its entry
- if (mMtpObjectHandle != 0) {
- entry.mRowId = 0;
- }
-
- if (entry.mPath != null) {
- if (((!mDefaultNotificationSet &&
- doesPathHaveFilename(entry.mPath, mDefaultNotificationFilename))
- || (!mDefaultRingtoneSet &&
- doesPathHaveFilename(entry.mPath, mDefaultRingtoneFilename))
- || (!mDefaultAlarmSet &&
- doesPathHaveFilename(entry.mPath, mDefaultAlarmAlertFilename)))) {
- Log.w(TAG, "forcing rescan of " + entry.mPath +
- "since ringtone setting didn't finish");
- scanAlways = true;
- } else if (isSystemSoundWithMetadata(entry.mPath)
- && !Build.FINGERPRINT.equals(sLastInternalScanFingerprint)) {
- // file is located on the system partition where the date cannot be trusted:
- // rescan if the build fingerprint has changed since the last scan.
- Log.i(TAG, "forcing rescan of " + entry.mPath
- + " since build fingerprint changed");
- scanAlways = true;
- }
- }
-
- // rescan for metadata if file was modified since last scan
- if (entry != null && (entry.mLastModifiedChanged || scanAlways)) {
- if (noMedia) {
- result = endFile(entry, false, false, false, false, false, false);
- } else {
- boolean isaudio = MediaFile.isAudioMimeType(mMimeType);
- boolean isvideo = MediaFile.isVideoMimeType(mMimeType);
- boolean isimage = MediaFile.isImageMimeType(mMimeType);
-
- if (isaudio || isvideo || isimage) {
- path = Environment.maybeTranslateEmulatedPathToInternal(new File(path))
- .getAbsolutePath();
- }
-
- // we only extract metadata for audio and video files
- if (isaudio || isvideo) {
- mScanSuccess = processFile(path, mimeType, this);
- }
-
- if (isimage) {
- mScanSuccess = processImageFile(path);
- }
-
- String lowpath = path.toLowerCase(Locale.ROOT);
- boolean ringtones = mScanSuccess && (lowpath.indexOf(RINGTONES_DIR) > 0);
- boolean notifications = mScanSuccess &&
- (lowpath.indexOf(NOTIFICATIONS_DIR) > 0);
- boolean alarms = mScanSuccess && (lowpath.indexOf(ALARMS_DIR) > 0);
- boolean podcasts = mScanSuccess && (lowpath.indexOf(PODCASTS_DIR) > 0);
- boolean audiobooks = mScanSuccess && (lowpath.indexOf(AUDIOBOOKS_DIR) > 0);
- boolean music = mScanSuccess && ((lowpath.indexOf(MUSIC_DIR) > 0) ||
- (!ringtones && !notifications && !alarms && !podcasts && !audiobooks));
-
- result = endFile(entry, ringtones, notifications, alarms, podcasts,
- audiobooks, music);
- }
- }
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e);
- }
-// long t2 = System.currentTimeMillis();
-// Log.v(TAG, "scanFile: " + path + " took " + (t2-t1));
- return result;
+ throw new UnsupportedOperationException();
}
- private long parseDate(String date) {
- try {
- return mDateFormatter.parse(date).getTime();
- } catch (ParseException e) {
- return 0;
- }
- }
-
- private int parseSubstring(String s, int start, int defaultValue) {
- int length = s.length();
- if (start == length) return defaultValue;
-
- char ch = s.charAt(start++);
- // return defaultValue if we have no integer at all
- if (ch < '0' || ch > '9') return defaultValue;
-
- int result = ch - '0';
- while (start < length) {
- ch = s.charAt(start++);
- if (ch < '0' || ch > '9') return result;
- result = result * 10 + (ch - '0');
- }
-
- return result;
- }
-
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
public void handleStringTag(String name, String value) {
- if (name.equalsIgnoreCase("title") || name.startsWith("title;")) {
- // Don't trim() here, to preserve the special \001 character
- // used to force sorting. The media provider will trim() before
- // inserting the title in to the database.
- mTitle = value;
- } else if (name.equalsIgnoreCase("artist") || name.startsWith("artist;")) {
- mArtist = value.trim();
- } else if (name.equalsIgnoreCase("albumartist") || name.startsWith("albumartist;")
- || name.equalsIgnoreCase("band") || name.startsWith("band;")) {
- mAlbumArtist = value.trim();
- } else if (name.equalsIgnoreCase("album") || name.startsWith("album;")) {
- mAlbum = value.trim();
- } else if (name.equalsIgnoreCase("composer") || name.startsWith("composer;")) {
- mComposer = value.trim();
- } else if (mProcessGenres &&
- (name.equalsIgnoreCase("genre") || name.startsWith("genre;"))) {
- mGenre = getGenreName(value);
- } else if (name.equalsIgnoreCase("year") || name.startsWith("year;")) {
- mYear = parseSubstring(value, 0, 0);
- } else if (name.equalsIgnoreCase("tracknumber") || name.startsWith("tracknumber;")) {
- // track number might be of the form "2/12"
- // we just read the number before the slash
- int num = parseSubstring(value, 0, 0);
- mTrack = (mTrack / 1000) * 1000 + num;
- } else if (name.equalsIgnoreCase("discnumber") ||
- name.equals("set") || name.startsWith("set;")) {
- // set number might be of the form "1/3"
- // we just read the number before the slash
- int num = parseSubstring(value, 0, 0);
- mTrack = (num * 1000) + (mTrack % 1000);
- } else if (name.equalsIgnoreCase("duration")) {
- mDuration = parseSubstring(value, 0, 0);
- } else if (name.equalsIgnoreCase("writer") || name.startsWith("writer;")) {
- mWriter = value.trim();
- } else if (name.equalsIgnoreCase("compilation")) {
- mCompilation = parseSubstring(value, 0, 0);
- } else if (name.equalsIgnoreCase("isdrm")) {
- mIsDrm = (parseSubstring(value, 0, 0) == 1);
- } else if (name.equalsIgnoreCase("date")) {
- mDate = parseDate(value);
- } else if (name.equalsIgnoreCase("width")) {
- mWidth = parseSubstring(value, 0, 0);
- } else if (name.equalsIgnoreCase("height")) {
- mHeight = parseSubstring(value, 0, 0);
- } else if (name.equalsIgnoreCase("colorstandard")) {
- mColorStandard = parseSubstring(value, 0, -1);
- } else if (name.equalsIgnoreCase("colortransfer")) {
- mColorTransfer = parseSubstring(value, 0, -1);
- } else if (name.equalsIgnoreCase("colorrange")) {
- mColorRange = parseSubstring(value, 0, -1);
- } else {
- //Log.v(TAG, "unknown tag: " + name + " (" + mProcessGenres + ")");
- }
+ throw new UnsupportedOperationException();
}
- private boolean convertGenreCode(String input, String expected) {
- String output = getGenreName(input);
- if (output.equals(expected)) {
- return true;
- } else {
- Log.d(TAG, "'" + input + "' -> '" + output + "', expected '" + expected + "'");
- return false;
- }
- }
- private void testGenreNameConverter() {
- convertGenreCode("2", "Country");
- convertGenreCode("(2)", "Country");
- convertGenreCode("(2", "(2");
- convertGenreCode("2 Foo", "Country");
- convertGenreCode("(2) Foo", "Country");
- convertGenreCode("(2 Foo", "(2 Foo");
- convertGenreCode("2Foo", "2Foo");
- convertGenreCode("(2)Foo", "Country");
- convertGenreCode("200 Foo", "Foo");
- convertGenreCode("(200) Foo", "Foo");
- convertGenreCode("200Foo", "200Foo");
- convertGenreCode("(200)Foo", "Foo");
- convertGenreCode("200)Foo", "200)Foo");
- convertGenreCode("200) Foo", "200) Foo");
- }
-
- public String getGenreName(String genreTagValue) {
-
- if (genreTagValue == null) {
- return null;
- }
- final int length = genreTagValue.length();
-
- if (length > 0) {
- boolean parenthesized = false;
- StringBuffer number = new StringBuffer();
- int i = 0;
- for (; i < length; ++i) {
- char c = genreTagValue.charAt(i);
- if (i == 0 && c == '(') {
- parenthesized = true;
- } else if (Character.isDigit(c)) {
- number.append(c);
- } else {
- break;
- }
- }
- char charAfterNumber = i < length ? genreTagValue.charAt(i) : ' ';
- if ((parenthesized && charAfterNumber == ')')
- || !parenthesized && Character.isWhitespace(charAfterNumber)) {
- try {
- short genreIndex = Short.parseShort(number.toString());
- if (genreIndex >= 0) {
- if (genreIndex < ID3_GENRES.length && ID3_GENRES[genreIndex] != null) {
- return ID3_GENRES[genreIndex];
- } else if (genreIndex == 0xFF) {
- return null;
- } else if (genreIndex < 0xFF && (i + 1) < length) {
- // genre is valid but unknown,
- // if there is a string after the value we take it
- if (parenthesized && charAfterNumber == ')') {
- i++;
- }
- String ret = genreTagValue.substring(i).trim();
- if (ret.length() != 0) {
- return ret;
- }
- } else {
- // else return the number, without parentheses
- return number.toString();
- }
- }
- } catch (NumberFormatException e) {
- }
- }
- }
-
- return genreTagValue;
- }
-
- private boolean processImageFile(String path) {
- try {
- mBitmapOptions.outWidth = 0;
- mBitmapOptions.outHeight = 0;
- BitmapFactory.decodeFile(path, mBitmapOptions);
- mWidth = mBitmapOptions.outWidth;
- mHeight = mBitmapOptions.outHeight;
- return mWidth > 0 && mHeight > 0;
- } catch (Throwable th) {
- // ignore;
- }
- return false;
- }
-
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
public void setMimeType(String mimeType) {
- if ("audio/mp4".equals(mMimeType) &&
- mimeType.startsWith("video")) {
- // for feature parity with Donut, we force m4a files to keep the
- // audio/mp4 mimetype, even if they are really "enhanced podcasts"
- // with a video track
- return;
- }
- mMimeType = mimeType;
+ throw new UnsupportedOperationException();
}
- /**
- * Formats the data into a values array suitable for use with the Media
- * Content Provider.
- *
- * @return a map of values
- */
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private ContentValues toValues() {
- ContentValues map = new ContentValues();
-
- map.put(MediaStore.MediaColumns.DATA, mPath);
- map.put(MediaStore.MediaColumns.TITLE, mTitle);
- map.put(MediaStore.MediaColumns.DATE_MODIFIED, mLastModified);
- map.put(MediaStore.MediaColumns.SIZE, mFileSize);
- map.put(MediaStore.MediaColumns.MIME_TYPE, mMimeType);
- map.put(MediaStore.MediaColumns.IS_DRM, mIsDrm);
- map.putNull(MediaStore.MediaColumns.HASH);
-
- String resolution = null;
- if (mWidth > 0 && mHeight > 0) {
- map.put(MediaStore.MediaColumns.WIDTH, mWidth);
- map.put(MediaStore.MediaColumns.HEIGHT, mHeight);
- resolution = mWidth + "x" + mHeight;
- }
-
- if (!mNoMedia) {
- if (MediaFile.isVideoMimeType(mMimeType)) {
- map.put(Video.Media.ARTIST, (mArtist != null && mArtist.length() > 0
- ? mArtist : MediaStore.UNKNOWN_STRING));
- map.put(Video.Media.ALBUM, (mAlbum != null && mAlbum.length() > 0
- ? mAlbum : MediaStore.UNKNOWN_STRING));
- map.put(Video.Media.DURATION, mDuration);
- if (resolution != null) {
- map.put(Video.Media.RESOLUTION, resolution);
- }
- if (mColorStandard >= 0) {
- map.put(Video.Media.COLOR_STANDARD, mColorStandard);
- }
- if (mColorTransfer >= 0) {
- map.put(Video.Media.COLOR_TRANSFER, mColorTransfer);
- }
- if (mColorRange >= 0) {
- map.put(Video.Media.COLOR_RANGE, mColorRange);
- }
- if (mDate > 0) {
- map.put(Video.Media.DATE_TAKEN, mDate);
- }
- } else if (MediaFile.isImageMimeType(mMimeType)) {
- // FIXME - add DESCRIPTION
- } else if (MediaFile.isAudioMimeType(mMimeType)) {
- map.put(Audio.Media.ARTIST, (mArtist != null && mArtist.length() > 0) ?
- mArtist : MediaStore.UNKNOWN_STRING);
- map.put(Audio.Media.ALBUM_ARTIST, (mAlbumArtist != null &&
- mAlbumArtist.length() > 0) ? mAlbumArtist : null);
- map.put(Audio.Media.ALBUM, (mAlbum != null && mAlbum.length() > 0) ?
- mAlbum : MediaStore.UNKNOWN_STRING);
- map.put(Audio.Media.COMPOSER, mComposer);
- map.put(Audio.Media.GENRE, mGenre);
- if (mYear != 0) {
- map.put(Audio.Media.YEAR, mYear);
- }
- map.put(Audio.Media.TRACK, mTrack);
- map.put(Audio.Media.DURATION, mDuration);
- map.put(Audio.Media.COMPILATION, mCompilation);
- }
- }
- return map;
+ throw new UnsupportedOperationException();
}
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private Uri endFile(FileEntry entry, boolean ringtones, boolean notifications,
boolean alarms, boolean podcasts, boolean audiobooks, boolean music)
throws RemoteException {
- // update database
-
- // use album artist if artist is missing
- if (mArtist == null || mArtist.length() == 0) {
- mArtist = mAlbumArtist;
- }
-
- ContentValues values = toValues();
- String title = values.getAsString(MediaStore.MediaColumns.TITLE);
- if (title == null || TextUtils.isEmpty(title.trim())) {
- title = MediaFile.getFileTitle(values.getAsString(MediaStore.MediaColumns.DATA));
- values.put(MediaStore.MediaColumns.TITLE, title);
- }
- String album = values.getAsString(Audio.Media.ALBUM);
- if (MediaStore.UNKNOWN_STRING.equals(album)) {
- album = values.getAsString(MediaStore.MediaColumns.DATA);
- // extract last path segment before file name
- int lastSlash = album.lastIndexOf('/');
- if (lastSlash >= 0) {
- int previousSlash = 0;
- while (true) {
- int idx = album.indexOf('/', previousSlash + 1);
- if (idx < 0 || idx >= lastSlash) {
- break;
- }
- previousSlash = idx;
- }
- if (previousSlash != 0) {
- album = album.substring(previousSlash + 1, lastSlash);
- values.put(Audio.Media.ALBUM, album);
- }
- }
- }
- long rowId = entry.mRowId;
- if (MediaFile.isAudioMimeType(mMimeType) && (rowId == 0 || mMtpObjectHandle != 0)) {
- // Only set these for new entries. For existing entries, they
- // may have been modified later, and we want to keep the current
- // values so that custom ringtones still show up in the ringtone
- // picker.
- values.put(Audio.Media.IS_RINGTONE, ringtones);
- values.put(Audio.Media.IS_NOTIFICATION, notifications);
- values.put(Audio.Media.IS_ALARM, alarms);
- values.put(Audio.Media.IS_MUSIC, music);
- values.put(Audio.Media.IS_PODCAST, podcasts);
- values.put(Audio.Media.IS_AUDIOBOOK, audiobooks);
- } else if (MediaFile.isExifMimeType(mMimeType) && !mNoMedia) {
- ExifInterface exif = null;
- try {
- exif = new ExifInterface(entry.mPath);
- } catch (Exception ex) {
- // exif is null
- }
- if (exif != null) {
- long time = exif.getGpsDateTime();
- if (time != -1) {
- values.put(Images.Media.DATE_TAKEN, time);
- } else {
- // If no time zone information is available, we should consider using
- // EXIF local time as taken time if the difference between file time
- // and EXIF local time is not less than 1 Day, otherwise MediaProvider
- // will use file time as taken time.
- time = exif.getDateTime();
- if (time != -1 && Math.abs(mLastModified * 1000 - time) >= 86400000) {
- values.put(Images.Media.DATE_TAKEN, time);
- }
- }
-
- int orientation = exif.getAttributeInt(
- ExifInterface.TAG_ORIENTATION, -1);
- if (orientation != -1) {
- // We only recognize a subset of orientation tag values.
- int degree;
- switch(orientation) {
- case ExifInterface.ORIENTATION_ROTATE_90:
- degree = 90;
- break;
- case ExifInterface.ORIENTATION_ROTATE_180:
- degree = 180;
- break;
- case ExifInterface.ORIENTATION_ROTATE_270:
- degree = 270;
- break;
- default:
- degree = 0;
- break;
- }
- values.put(Images.Media.ORIENTATION, degree);
- }
- }
- }
-
- Uri tableUri = mFilesUri;
- int mediaType = FileColumns.MEDIA_TYPE_NONE;
- MediaInserter inserter = mMediaInserter;
- if (!mNoMedia) {
- if (MediaFile.isVideoMimeType(mMimeType)) {
- tableUri = mVideoUri;
- mediaType = FileColumns.MEDIA_TYPE_VIDEO;
- } else if (MediaFile.isImageMimeType(mMimeType)) {
- tableUri = mImagesUri;
- mediaType = FileColumns.MEDIA_TYPE_IMAGE;
- } else if (MediaFile.isAudioMimeType(mMimeType)) {
- tableUri = mAudioUri;
- mediaType = FileColumns.MEDIA_TYPE_AUDIO;
- } else if (MediaFile.isPlayListMimeType(mMimeType)) {
- tableUri = mPlaylistsUri;
- mediaType = FileColumns.MEDIA_TYPE_PLAYLIST;
- }
- }
- Uri result = null;
- boolean needToSetSettings = false;
- // Setting a flag in order not to use bulk insert for the file related with
- // notifications, ringtones, and alarms, because the rowId of the inserted file is
- // needed.
- if (notifications && !mDefaultNotificationSet) {
- if (TextUtils.isEmpty(mDefaultNotificationFilename) ||
- doesPathHaveFilename(entry.mPath, mDefaultNotificationFilename)) {
- needToSetSettings = true;
- }
- } else if (ringtones && !mDefaultRingtoneSet) {
- if (TextUtils.isEmpty(mDefaultRingtoneFilename) ||
- doesPathHaveFilename(entry.mPath, mDefaultRingtoneFilename)) {
- needToSetSettings = true;
- }
- } else if (alarms && !mDefaultAlarmSet) {
- if (TextUtils.isEmpty(mDefaultAlarmAlertFilename) ||
- doesPathHaveFilename(entry.mPath, mDefaultAlarmAlertFilename)) {
- needToSetSettings = true;
- }
- }
-
- if (rowId == 0) {
- if (mMtpObjectHandle != 0) {
- values.put(MediaStore.MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID, mMtpObjectHandle);
- }
- if (tableUri == mFilesUri) {
- int format = entry.mFormat;
- if (format == 0) {
- format = MediaFile.getFormatCode(entry.mPath, mMimeType);
- }
- values.put(Files.FileColumns.FORMAT, format);
- }
- // New file, insert it.
- // Directories need to be inserted before the files they contain, so they
- // get priority when bulk inserting.
- // If the rowId of the inserted file is needed, it gets inserted immediately,
- // bypassing the bulk inserter.
- if (inserter == null || needToSetSettings) {
- if (inserter != null) {
- inserter.flushAll();
- }
- result = mMediaProvider.insert(tableUri, values);
- } else if (entry.mFormat == MtpConstants.FORMAT_ASSOCIATION) {
- inserter.insertwithPriority(tableUri, values);
- } else {
- inserter.insert(tableUri, values);
- }
-
- if (result != null) {
- rowId = ContentUris.parseId(result);
- entry.mRowId = rowId;
- }
- } else {
- // updated file
- result = ContentUris.withAppendedId(tableUri, rowId);
- // path should never change, and we want to avoid replacing mixed cased paths
- // with squashed lower case paths
- values.remove(MediaStore.MediaColumns.DATA);
-
- if (!mNoMedia) {
- // Changing media type must be done as separate update
- if (mediaType != entry.mMediaType) {
- final ContentValues mediaTypeValues = new ContentValues();
- mediaTypeValues.put(FileColumns.MEDIA_TYPE, mediaType);
- mMediaProvider.update(ContentUris.withAppendedId(mFilesUri, rowId),
- mediaTypeValues, null, null);
- }
- }
-
- mMediaProvider.update(result, values, null, null);
- }
-
- if(needToSetSettings) {
- if (notifications) {
- setRingtoneIfNotSet(Settings.System.NOTIFICATION_SOUND, tableUri, rowId);
- mDefaultNotificationSet = true;
- } else if (ringtones) {
- setRingtoneIfNotSet(Settings.System.RINGTONE, tableUri, rowId);
- mDefaultRingtoneSet = true;
- } else if (alarms) {
- setRingtoneIfNotSet(Settings.System.ALARM_ALERT, tableUri, rowId);
- mDefaultAlarmSet = true;
- }
- }
-
- return result;
- }
-
- private boolean doesPathHaveFilename(String path, String filename) {
- int pathFilenameStart = path.lastIndexOf(File.separatorChar) + 1;
- int filenameLength = filename.length();
- return path.regionMatches(pathFilenameStart, filename, 0, filenameLength) &&
- pathFilenameStart + filenameLength == path.length();
+ throw new UnsupportedOperationException();
}
- private void setRingtoneIfNotSet(String settingName, Uri uri, long rowId) {
- if (wasRingtoneAlreadySet(settingName)) {
- return;
- }
-
- ContentResolver cr = mContext.getContentResolver();
- String existingSettingValue = Settings.System.getString(cr, settingName);
- if (TextUtils.isEmpty(existingSettingValue)) {
- final Uri settingUri = Settings.System.getUriFor(settingName);
- final Uri ringtoneUri = ContentUris.withAppendedId(uri, rowId);
- RingtoneManager.setActualDefaultRingtoneUri(mContext,
- RingtoneManager.getDefaultType(settingUri), ringtoneUri);
- }
- Settings.System.putInt(cr, settingSetIndicatorName(settingName), 1);
- }
-
- /** @deprecated file types no longer exist */
@Deprecated
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private int getFileTypeFromDrm(String path) {
- return 0;
+ throw new UnsupportedOperationException();
}
-
- private void getMimeTypeFromDrm(String path) {
- mMimeType = null;
-
- if (mDrmManagerClient == null) {
- mDrmManagerClient = new DrmManagerClient(mContext);
- }
-
- if (mDrmManagerClient.canHandle(path, null)) {
- mIsDrm = true;
- mMimeType = mDrmManagerClient.getOriginalMimeType(path);
- }
-
- if (mMimeType == null) {
- mMimeType = ContentResolver.MIME_TYPE_DEFAULT;
- }
- }
-
- }; // end of anonymous MediaScannerClient instance
-
- private static boolean isSystemSoundWithMetadata(String path) {
- if (path.startsWith(SYSTEM_SOUNDS_DIR + ALARMS_DIR)
- || path.startsWith(SYSTEM_SOUNDS_DIR + RINGTONES_DIR)
- || path.startsWith(SYSTEM_SOUNDS_DIR + NOTIFICATIONS_DIR)
- || path.startsWith(OEM_SOUNDS_DIR + ALARMS_DIR)
- || path.startsWith(OEM_SOUNDS_DIR + RINGTONES_DIR)
- || path.startsWith(OEM_SOUNDS_DIR + NOTIFICATIONS_DIR)
- || path.startsWith(PRODUCT_SOUNDS_DIR + ALARMS_DIR)
- || path.startsWith(PRODUCT_SOUNDS_DIR + RINGTONES_DIR)
- || path.startsWith(PRODUCT_SOUNDS_DIR + NOTIFICATIONS_DIR)) {
- return true;
- }
- return false;
}
- private String settingSetIndicatorName(String base) {
- return base + "_set";
- }
-
- private boolean wasRingtoneAlreadySet(String name) {
- ContentResolver cr = mContext.getContentResolver();
- String indicatorName = settingSetIndicatorName(name);
- try {
- return Settings.System.getInt(cr, indicatorName) != 0;
- } catch (SettingNotFoundException e) {
- return false;
- }
- }
-
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private void prescan(String filePath, boolean prescanFiles) throws RemoteException {
- Cursor c = null;
- String where = null;
- String[] selectionArgs = null;
-
- mPlayLists.clear();
-
- if (filePath != null) {
- // query for only one file
- where = MediaStore.Files.FileColumns._ID + ">?" +
- " AND " + Files.FileColumns.DATA + "=?";
- selectionArgs = new String[] { "", filePath };
- } else {
- where = MediaStore.Files.FileColumns._ID + ">?";
- selectionArgs = new String[] { "" };
- }
-
- mDefaultRingtoneSet = wasRingtoneAlreadySet(Settings.System.RINGTONE);
- mDefaultNotificationSet = wasRingtoneAlreadySet(Settings.System.NOTIFICATION_SOUND);
- mDefaultAlarmSet = wasRingtoneAlreadySet(Settings.System.ALARM_ALERT);
-
- // Tell the provider to not delete the file.
- // If the file is truly gone the delete is unnecessary, and we want to avoid
- // accidentally deleting files that are really there (this may happen if the
- // filesystem is mounted and unmounted while the scanner is running).
- Uri.Builder builder = mFilesUri.buildUpon();
- builder.appendQueryParameter(MediaStore.PARAM_DELETE_DATA, "false");
- MediaBulkDeleter deleter = new MediaBulkDeleter(mMediaProvider, builder.build());
-
- // Build the list of files from the content provider
- try {
- if (prescanFiles) {
- // First read existing files from the files table.
- // Because we'll be deleting entries for missing files as we go,
- // we need to query the database in small batches, to avoid problems
- // with CursorWindow positioning.
- long lastId = Long.MIN_VALUE;
- Uri limitUri = mFilesUri.buildUpon()
- .appendQueryParameter(MediaStore.PARAM_LIMIT, "1000").build();
-
- while (true) {
- selectionArgs[0] = "" + lastId;
- if (c != null) {
- c.close();
- c = null;
- }
- c = mMediaProvider.query(limitUri, FILES_PRESCAN_PROJECTION,
- where, selectionArgs, MediaStore.Files.FileColumns._ID, null);
- if (c == null) {
- break;
- }
-
- int num = c.getCount();
-
- if (num == 0) {
- break;
- }
- while (c.moveToNext()) {
- long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX);
- String path = c.getString(FILES_PRESCAN_PATH_COLUMN_INDEX);
- int format = c.getInt(FILES_PRESCAN_FORMAT_COLUMN_INDEX);
- long lastModified = c.getLong(FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
- lastId = rowId;
-
- // Only consider entries with absolute path names.
- // This allows storing URIs in the database without the
- // media scanner removing them.
- if (path != null && path.startsWith("/")) {
- boolean exists = false;
- try {
- exists = Os.access(path, android.system.OsConstants.F_OK);
- } catch (ErrnoException e1) {
- }
- if (!exists && !MtpConstants.isAbstractObject(format)) {
- // do not delete missing playlists, since they may have been
- // modified by the user.
- // The user can delete them in the media player instead.
- // instead, clear the path and lastModified fields in the row
- String mimeType = MediaFile.getMimeTypeForFile(path);
- if (!MediaFile.isPlayListMimeType(mimeType)) {
- deleter.delete(rowId);
- if (path.toLowerCase(Locale.US).endsWith("/.nomedia")) {
- deleter.flush();
- String parent = new File(path).getParent();
- mMediaProvider.call(MediaStore.UNHIDE_CALL, parent, null);
- }
- }
- }
- }
- }
- }
- }
- }
- finally {
- if (c != null) {
- c.close();
- }
- deleter.flush();
- }
-
- // compute original size of images
- mOriginalCount = 0;
- c = mMediaProvider.query(mImagesUri, ID_PROJECTION, null, null, null, null);
- if (c != null) {
- mOriginalCount = c.getCount();
- c.close();
- }
- }
-
- static class MediaBulkDeleter {
- StringBuilder whereClause = new StringBuilder();
- ArrayList<String> whereArgs = new ArrayList<String>(100);
- final ContentProviderClient mProvider;
- final Uri mBaseUri;
-
- public MediaBulkDeleter(ContentProviderClient provider, Uri baseUri) {
- mProvider = provider;
- mBaseUri = baseUri;
- }
-
- public void delete(long id) throws RemoteException {
- if (whereClause.length() != 0) {
- whereClause.append(",");
- }
- whereClause.append("?");
- whereArgs.add("" + id);
- if (whereArgs.size() > 100) {
- flush();
- }
- }
- public void flush() throws RemoteException {
- int size = whereArgs.size();
- if (size > 0) {
- String [] foo = new String [size];
- foo = whereArgs.toArray(foo);
- int numrows = mProvider.delete(mBaseUri,
- MediaStore.MediaColumns._ID + " IN (" +
- whereClause.toString() + ")", foo);
- //Log.i("@@@@@@@@@", "rows deleted: " + numrows);
- whereClause.setLength(0);
- whereArgs.clear();
- }
- }
+ throw new UnsupportedOperationException();
}
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private void postscan(final String[] directories) throws RemoteException {
-
- // handle playlists last, after we know what media files are on the storage.
- if (mProcessPlaylists) {
- processPlayLists();
- }
-
- // allow GC to clean up
- mPlayLists.clear();
- }
-
- private void releaseResources() {
- // release the DrmManagerClient resources
- if (mDrmManagerClient != null) {
- mDrmManagerClient.close();
- mDrmManagerClient = null;
- }
- }
-
- public void scanDirectories(String[] directories) {
- try {
- long start = System.currentTimeMillis();
- prescan(null, true);
- long prescan = System.currentTimeMillis();
-
- if (ENABLE_BULK_INSERTS) {
- // create MediaInserter for bulk inserts
- mMediaInserter = new MediaInserter(mMediaProvider, 500);
- }
-
- for (int i = 0; i < directories.length; i++) {
- processDirectory(directories[i], mClient);
- }
-
- if (ENABLE_BULK_INSERTS) {
- // flush remaining inserts
- mMediaInserter.flushAll();
- mMediaInserter = null;
- }
-
- long scan = System.currentTimeMillis();
- postscan(directories);
- long end = System.currentTimeMillis();
-
- if (false) {
- Log.d(TAG, " prescan time: " + (prescan - start) + "ms\n");
- Log.d(TAG, " scan time: " + (scan - prescan) + "ms\n");
- Log.d(TAG, "postscan time: " + (end - scan) + "ms\n");
- Log.d(TAG, " total time: " + (end - start) + "ms\n");
- }
- } catch (SQLException e) {
- // this might happen if the SD card is removed while the media scanner is running
- Log.e(TAG, "SQLException in MediaScanner.scan()", e);
- } catch (UnsupportedOperationException e) {
- // this might happen if the SD card is removed while the media scanner is running
- Log.e(TAG, "UnsupportedOperationException in MediaScanner.scan()", e);
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in MediaScanner.scan()", e);
- } finally {
- releaseResources();
- }
+ throw new UnsupportedOperationException();
}
- // this function is used to scan a single file
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
public Uri scanSingleFile(String path, String mimeType) {
- try {
- prescan(path, true);
-
- File file = new File(path);
- if (!file.exists() || !file.canRead()) {
- return null;
- }
-
- // lastModified is in milliseconds on Files.
- long lastModifiedSeconds = file.lastModified() / 1000;
-
- // always scan the file, so we can return the content://media Uri for existing files
- return mClient.doScanFile(path, mimeType, lastModifiedSeconds, file.length(),
- false, true, MediaScanner.isNoMediaPath(path));
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e);
- return null;
- } finally {
- releaseResources();
- }
- }
-
- private static boolean isNoMediaFile(String path) {
- File file = new File(path);
- if (file.isDirectory()) return false;
-
- // special case certain file names
- // I use regionMatches() instead of substring() below
- // to avoid memory allocation
- int lastSlash = path.lastIndexOf('/');
- if (lastSlash >= 0 && lastSlash + 2 < path.length()) {
- // ignore those ._* files created by MacOS
- if (path.regionMatches(lastSlash + 1, "._", 0, 2)) {
- return true;
- }
-
- // ignore album art files created by Windows Media Player:
- // Folder.jpg, AlbumArtSmall.jpg, AlbumArt_{...}_Large.jpg
- // and AlbumArt_{...}_Small.jpg
- if (path.regionMatches(true, path.length() - 4, ".jpg", 0, 4)) {
- if (path.regionMatches(true, lastSlash + 1, "AlbumArt_{", 0, 10) ||
- path.regionMatches(true, lastSlash + 1, "AlbumArt.", 0, 9)) {
- return true;
- }
- int length = path.length() - lastSlash - 1;
- if ((length == 17 && path.regionMatches(
- true, lastSlash + 1, "AlbumArtSmall", 0, 13)) ||
- (length == 10
- && path.regionMatches(true, lastSlash + 1, "Folder", 0, 6))) {
- return true;
- }
- }
- }
- return false;
- }
-
- private static HashMap<String,String> mNoMediaPaths = new HashMap<String,String>();
- private static HashMap<String,String> mMediaPaths = new HashMap<String,String>();
-
- /* MediaProvider calls this when a .nomedia file is added or removed */
- public static void clearMediaPathCache(boolean clearMediaPaths, boolean clearNoMediaPaths) {
- synchronized (MediaScanner.class) {
- if (clearMediaPaths) {
- mMediaPaths.clear();
- }
- if (clearNoMediaPaths) {
- mNoMediaPaths.clear();
- }
- }
+ throw new UnsupportedOperationException();
}
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
public static boolean isNoMediaPath(String path) {
- if (path == null) {
- return false;
- }
- // return true if file or any parent directory has name starting with a dot
- if (path.indexOf("/.") >= 0) {
- return true;
- }
-
- int firstSlash = path.lastIndexOf('/');
- if (firstSlash <= 0) {
- return false;
- }
- String parent = path.substring(0, firstSlash);
-
- synchronized (MediaScanner.class) {
- if (mNoMediaPaths.containsKey(parent)) {
- return true;
- } else if (!mMediaPaths.containsKey(parent)) {
- // check to see if any parent directories have a ".nomedia" file
- // start from 1 so we don't bother checking in the root directory
- int offset = 1;
- while (offset >= 0) {
- int slashIndex = path.indexOf('/', offset);
- if (slashIndex > offset) {
- slashIndex++; // move past slash
- File file = new File(path.substring(0, slashIndex) + ".nomedia");
- if (file.exists()) {
- // we have a .nomedia in one of the parent directories
- mNoMediaPaths.put(parent, "");
- return true;
- }
- }
- offset = slashIndex;
- }
- mMediaPaths.put(parent, "");
- }
- }
-
- return isNoMediaFile(path);
- }
-
- public void scanMtpFile(String path, int objectHandle, int format) {
- String mimeType = MediaFile.getMimeType(path, format);
- File file = new File(path);
- long lastModifiedSeconds = file.lastModified() / 1000;
-
- if (!MediaFile.isAudioMimeType(mimeType) && !MediaFile.isVideoMimeType(mimeType) &&
- !MediaFile.isImageMimeType(mimeType) && !MediaFile.isPlayListMimeType(mimeType) &&
- !MediaFile.isDrmMimeType(mimeType)) {
-
- // no need to use the media scanner, but we need to update last modified and file size
- ContentValues values = new ContentValues();
- values.put(Files.FileColumns.SIZE, file.length());
- values.put(Files.FileColumns.DATE_MODIFIED, lastModifiedSeconds);
- try {
- String[] whereArgs = new String[] { Integer.toString(objectHandle) };
- mMediaProvider.update(Files.getMtpObjectsUri(mVolumeName), values,
- "_id=?", whereArgs);
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in scanMtpFile", e);
- }
- return;
- }
-
- mMtpObjectHandle = objectHandle;
- Cursor fileList = null;
- try {
- if (MediaFile.isPlayListMimeType(mimeType)) {
- // build file cache so we can look up tracks in the playlist
- prescan(null, true);
-
- FileEntry entry = makeEntryFor(path);
- if (entry != null) {
- fileList = mMediaProvider.query(mFilesUri,
- FILES_PRESCAN_PROJECTION, null, null, null, null);
- processPlayList(entry, fileList);
- }
- } else {
- // MTP will create a file entry for us so we don't want to do it in prescan
- prescan(path, false);
-
- // always scan the file, so we can return the content://media Uri for existing files
- mClient.doScanFile(path, mimeType, lastModifiedSeconds, file.length(),
- (format == MtpConstants.FORMAT_ASSOCIATION), true, isNoMediaPath(path));
- }
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e);
- } finally {
- mMtpObjectHandle = 0;
- if (fileList != null) {
- fileList.close();
- }
- releaseResources();
- }
+ throw new UnsupportedOperationException();
}
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
FileEntry makeEntryFor(String path) {
- String where;
- String[] selectionArgs;
-
- Cursor c = null;
- try {
- where = Files.FileColumns.DATA + "=?";
- selectionArgs = new String[] { path };
- c = mMediaProvider.query(mFilesFullUri, FILES_PRESCAN_PROJECTION,
- where, selectionArgs, null, null);
- if (c != null && c.moveToFirst()) {
- long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX);
- long lastModified = c.getLong(FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
- int format = c.getInt(FILES_PRESCAN_FORMAT_COLUMN_INDEX);
- int mediaType = c.getInt(FILES_PRESCAN_MEDIA_TYPE_COLUMN_INDEX);
- return new FileEntry(rowId, path, lastModified, format, mediaType);
- }
- } catch (RemoteException e) {
- } finally {
- if (c != null) {
- c.close();
- }
- }
- return null;
+ throw new UnsupportedOperationException();
}
- // returns the number of matching file/directory names, starting from the right
- private int matchPaths(String path1, String path2) {
- int result = 0;
- int end1 = path1.length();
- int end2 = path2.length();
-
- while (end1 > 0 && end2 > 0) {
- int slash1 = path1.lastIndexOf('/', end1 - 1);
- int slash2 = path2.lastIndexOf('/', end2 - 1);
- int backSlash1 = path1.lastIndexOf('\\', end1 - 1);
- int backSlash2 = path2.lastIndexOf('\\', end2 - 1);
- int start1 = (slash1 > backSlash1 ? slash1 : backSlash1);
- int start2 = (slash2 > backSlash2 ? slash2 : backSlash2);
- if (start1 < 0) start1 = 0; else start1++;
- if (start2 < 0) start2 = 0; else start2++;
- int length = end1 - start1;
- if (end2 - start2 != length) break;
- if (path1.regionMatches(true, start1, path2, start2, length)) {
- result++;
- end1 = start1 - 1;
- end2 = start2 - 1;
- } else break;
- }
-
- return result;
- }
-
- private boolean matchEntries(long rowId, String data) {
-
- int len = mPlaylistEntries.size();
- boolean done = true;
- for (int i = 0; i < len; i++) {
- PlaylistEntry entry = mPlaylistEntries.get(i);
- if (entry.bestmatchlevel == Integer.MAX_VALUE) {
- continue; // this entry has been matched already
- }
- done = false;
- if (data.equalsIgnoreCase(entry.path)) {
- entry.bestmatchid = rowId;
- entry.bestmatchlevel = Integer.MAX_VALUE;
- continue; // no need for path matching
- }
-
- int matchLength = matchPaths(data, entry.path);
- if (matchLength > entry.bestmatchlevel) {
- entry.bestmatchid = rowId;
- entry.bestmatchlevel = matchLength;
- }
- }
- return done;
- }
-
- private void cachePlaylistEntry(String line, String playListDirectory) {
- PlaylistEntry entry = new PlaylistEntry();
- // watch for trailing whitespace
- int entryLength = line.length();
- while (entryLength > 0 && Character.isWhitespace(line.charAt(entryLength - 1))) entryLength--;
- // path should be longer than 3 characters.
- // avoid index out of bounds errors below by returning here.
- if (entryLength < 3) return;
- if (entryLength < line.length()) line = line.substring(0, entryLength);
-
- // does entry appear to be an absolute path?
- // look for Unix or DOS absolute paths
- char ch1 = line.charAt(0);
- boolean fullPath = (ch1 == '/' ||
- (Character.isLetter(ch1) && line.charAt(1) == ':' && line.charAt(2) == '\\'));
- // if we have a relative path, combine entry with playListDirectory
- if (!fullPath)
- line = playListDirectory + line;
- entry.path = line;
- //FIXME - should we look for "../" within the path?
-
- mPlaylistEntries.add(entry);
- }
-
- private void processCachedPlaylist(Cursor fileList, ContentValues values, Uri playlistUri) {
- fileList.moveToPosition(-1);
- while (fileList.moveToNext()) {
- long rowId = fileList.getLong(FILES_PRESCAN_ID_COLUMN_INDEX);
- String data = fileList.getString(FILES_PRESCAN_PATH_COLUMN_INDEX);
- if (matchEntries(rowId, data)) {
- break;
- }
- }
-
- int len = mPlaylistEntries.size();
- int index = 0;
- for (int i = 0; i < len; i++) {
- PlaylistEntry entry = mPlaylistEntries.get(i);
- if (entry.bestmatchlevel > 0) {
- try {
- values.clear();
- values.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER, Integer.valueOf(index));
- values.put(MediaStore.Audio.Playlists.Members.AUDIO_ID, Long.valueOf(entry.bestmatchid));
- mMediaProvider.insert(playlistUri, values);
- index++;
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in MediaScanner.processCachedPlaylist()", e);
- return;
- }
- }
- }
- mPlaylistEntries.clear();
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
+ private void setLocale(String locale) {
+ throw new UnsupportedOperationException();
}
- private void processM3uPlayList(String path, String playListDirectory, Uri uri,
- ContentValues values, Cursor fileList) {
- BufferedReader reader = null;
- try {
- File f = new File(path);
- if (f.exists()) {
- reader = new BufferedReader(
- new InputStreamReader(new FileInputStream(f)), 8192);
- String line = reader.readLine();
- mPlaylistEntries.clear();
- while (line != null) {
- // ignore comment lines, which begin with '#'
- if (line.length() > 0 && line.charAt(0) != '#') {
- cachePlaylistEntry(line, playListDirectory);
- }
- line = reader.readLine();
- }
-
- processCachedPlaylist(fileList, values, uri);
- }
- } catch (IOException e) {
- Log.e(TAG, "IOException in MediaScanner.processM3uPlayList()", e);
- } finally {
- try {
- if (reader != null)
- reader.close();
- } catch (IOException e) {
- Log.e(TAG, "IOException in MediaScanner.processM3uPlayList()", e);
- }
- }
- }
-
- private void processPlsPlayList(String path, String playListDirectory, Uri uri,
- ContentValues values, Cursor fileList) {
- BufferedReader reader = null;
- try {
- File f = new File(path);
- if (f.exists()) {
- reader = new BufferedReader(
- new InputStreamReader(new FileInputStream(f)), 8192);
- String line = reader.readLine();
- mPlaylistEntries.clear();
- while (line != null) {
- // ignore comment lines, which begin with '#'
- if (line.startsWith("File")) {
- int equals = line.indexOf('=');
- if (equals > 0) {
- cachePlaylistEntry(line.substring(equals + 1), playListDirectory);
- }
- }
- line = reader.readLine();
- }
-
- processCachedPlaylist(fileList, values, uri);
- }
- } catch (IOException e) {
- Log.e(TAG, "IOException in MediaScanner.processPlsPlayList()", e);
- } finally {
- try {
- if (reader != null)
- reader.close();
- } catch (IOException e) {
- Log.e(TAG, "IOException in MediaScanner.processPlsPlayList()", e);
- }
- }
- }
-
- class WplHandler implements ElementListener {
-
- final ContentHandler handler;
- String playListDirectory;
-
- public WplHandler(String playListDirectory, Uri uri, Cursor fileList) {
- this.playListDirectory = playListDirectory;
-
- RootElement root = new RootElement("smil");
- Element body = root.getChild("body");
- Element seq = body.getChild("seq");
- Element media = seq.getChild("media");
- media.setElementListener(this);
-
- this.handler = root.getContentHandler();
- }
-
- @Override
- public void start(Attributes attributes) {
- String path = attributes.getValue("", "src");
- if (path != null) {
- cachePlaylistEntry(path, playListDirectory);
- }
- }
-
- @Override
- public void end() {
- }
-
- ContentHandler getContentHandler() {
- return handler;
- }
- }
-
- private void processWplPlayList(String path, String playListDirectory, Uri uri,
- ContentValues values, Cursor fileList) {
- FileInputStream fis = null;
- try {
- File f = new File(path);
- if (f.exists()) {
- fis = new FileInputStream(f);
-
- mPlaylistEntries.clear();
- Xml.parse(fis, Xml.findEncodingByName("UTF-8"),
- new WplHandler(playListDirectory, uri, fileList).getContentHandler());
-
- processCachedPlaylist(fileList, values, uri);
- }
- } catch (SAXException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- try {
- if (fis != null)
- fis.close();
- } catch (IOException e) {
- Log.e(TAG, "IOException in MediaScanner.processWplPlayList()", e);
- }
- }
- }
-
- private void processPlayList(FileEntry entry, Cursor fileList) throws RemoteException {
- String path = entry.mPath;
- ContentValues values = new ContentValues();
- int lastSlash = path.lastIndexOf('/');
- if (lastSlash < 0) throw new IllegalArgumentException("bad path " + path);
- Uri uri, membersUri;
- long rowId = entry.mRowId;
-
- // make sure we have a name
- String name = values.getAsString(MediaStore.Audio.Playlists.NAME);
- if (name == null) {
- name = values.getAsString(MediaStore.MediaColumns.TITLE);
- if (name == null) {
- // extract name from file name
- int lastDot = path.lastIndexOf('.');
- name = (lastDot < 0 ? path.substring(lastSlash + 1)
- : path.substring(lastSlash + 1, lastDot));
- }
- }
-
- values.put(MediaStore.Audio.Playlists.NAME, name);
- values.put(MediaStore.Audio.Playlists.DATE_MODIFIED, entry.mLastModified);
-
- if (rowId == 0) {
- values.put(MediaStore.Audio.Playlists.DATA, path);
- uri = mMediaProvider.insert(mPlaylistsUri, values);
- rowId = ContentUris.parseId(uri);
- membersUri = Uri.withAppendedPath(uri, Playlists.Members.CONTENT_DIRECTORY);
- } else {
- uri = ContentUris.withAppendedId(mPlaylistsUri, rowId);
- mMediaProvider.update(uri, values, null, null);
-
- // delete members of existing playlist
- membersUri = Uri.withAppendedPath(uri, Playlists.Members.CONTENT_DIRECTORY);
- mMediaProvider.delete(membersUri, null, null);
- }
-
- String playListDirectory = path.substring(0, lastSlash + 1);
- String mimeType = MediaFile.getMimeTypeForFile(path);
- switch (mimeType) {
- case "application/vnd.ms-wpl":
- processWplPlayList(path, playListDirectory, membersUri, values, fileList);
- break;
- case "audio/x-mpegurl":
- processM3uPlayList(path, playListDirectory, membersUri, values, fileList);
- break;
- case "audio/x-scpls":
- processPlsPlayList(path, playListDirectory, membersUri, values, fileList);
- break;
- }
- }
-
- private void processPlayLists() throws RemoteException {
- Iterator<FileEntry> iterator = mPlayLists.iterator();
- Cursor fileList = null;
- try {
- // use the files uri and projection because we need the format column,
- // but restrict the query to just audio files
- fileList = mMediaProvider.query(mFilesUri, FILES_PRESCAN_PROJECTION,
- "media_type=2", null, null, null);
- while (iterator.hasNext()) {
- FileEntry entry = iterator.next();
- // only process playlist files if they are new or have been modified since the last scan
- if (entry.mLastModifiedChanged) {
- processPlayList(entry, fileList);
- }
- }
- } catch (RemoteException e1) {
- } finally {
- if (fileList != null) {
- fileList.close();
- }
- }
- }
-
- private native void processDirectory(String path, MediaScannerClient client);
- private native boolean processFile(String path, String mimeType, MediaScannerClient client);
- @UnsupportedAppUsage
- private native void setLocale(String locale);
-
- public native byte[] extractAlbumArt(FileDescriptor fd);
-
- private static native final void native_init();
- private native final void native_setup();
- private native final void native_finalize();
-
@Override
public void close() {
- mCloseGuard.close();
- if (mClosed.compareAndSet(false, true)) {
- mMediaProvider.close();
- native_finalize();
- }
- }
-
- @Override
- protected void finalize() throws Throwable {
- try {
- if (mCloseGuard != null) {
- mCloseGuard.warnIfOpen();
- }
-
- close();
- } finally {
- super.finalize();
- }
+ throw new UnsupportedOperationException();
}
}
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 435d8d766149..ff4044220428 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -846,7 +846,7 @@ public class RingtoneManager {
* Adds an audio file to the list of ringtones.
*
* After making sure the given file is an audio file, copies the file to the ringtone storage,
- * and asks the {@link android.media.MediaScanner} to scan that file. This call will block until
+ * and asks the system to scan that file. This call will block until
* the scan is completed.
*
* The directory where the copied file is stored is the directory that matches the ringtone's
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 45ee210c80c9..bd9ea13af046 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -17,7 +17,6 @@ cc_library_shared {
"android_media_MediaPlayer.cpp",
"android_media_MediaProfiles.cpp",
"android_media_MediaRecorder.cpp",
- "android_media_MediaScanner.cpp",
"android_media_MediaSync.cpp",
"android_media_ResampleInputStream.cpp",
"android_media_Streams.cpp",
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index d24edc7552ae..94299bc8431a 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -1446,7 +1446,6 @@ extern int register_android_media_MediaHTTPConnection(JNIEnv *env);
extern int register_android_media_MediaMetadataRetriever(JNIEnv *env);
extern int register_android_media_MediaMuxer(JNIEnv *env);
extern int register_android_media_MediaRecorder(JNIEnv *env);
-extern int register_android_media_MediaScanner(JNIEnv *env);
extern int register_android_media_MediaSync(JNIEnv *env);
extern int register_android_media_ResampleInputStream(JNIEnv *env);
extern int register_android_media_MediaProfiles(JNIEnv *env);
@@ -1485,11 +1484,6 @@ jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
goto bail;
}
- if (register_android_media_MediaScanner(env) < 0) {
- ALOGE("ERROR: MediaScanner native registration failed\n");
- goto bail;
- }
-
if (register_android_media_MediaMetadataRetriever(env) < 0) {
ALOGE("ERROR: MediaMetadataRetriever native registration failed\n");
goto bail;
diff --git a/media/jni/android_media_MediaScanner.cpp b/media/jni/android_media_MediaScanner.cpp
deleted file mode 100644
index 58044c0307eb..000000000000
--- a/media/jni/android_media_MediaScanner.cpp
+++ /dev/null
@@ -1,468 +0,0 @@
-/*
-**
-** Copyright 2007, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "MediaScannerJNI"
-#include <utils/Log.h>
-#include <utils/threads.h>
-#include <media/mediascanner.h>
-#include <media/stagefright/StagefrightMediaScanner.h>
-#include <private/media/VideoFrame.h>
-
-#include "jni.h"
-#include <nativehelper/JNIHelp.h>
-#include "android_runtime/AndroidRuntime.h"
-#include "android_runtime/Log.h"
-#include <android-base/macros.h> // for FALLTHROUGH_INTENDED
-
-using namespace android;
-
-
-static const char* const kClassMediaScannerClient =
- "android/media/MediaScannerClient";
-
-static const char* const kClassMediaScanner =
- "android/media/MediaScanner";
-
-static const char* const kRunTimeException =
- "java/lang/RuntimeException";
-
-static const char* const kIllegalArgumentException =
- "java/lang/IllegalArgumentException";
-
-struct fields_t {
- jfieldID context;
-};
-static fields_t fields;
-
-static status_t checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
- if (env->ExceptionCheck()) {
- ALOGE("An exception was thrown by callback '%s'.", methodName);
- LOGE_EX(env);
- env->ExceptionClear();
- return UNKNOWN_ERROR;
- }
- return OK;
-}
-
-// stolen from dalvik/vm/checkJni.cpp
-static bool isValidUtf8(const char* bytes) {
- while (*bytes != '\0') {
- unsigned char utf8 = *(bytes++);
- // Switch on the high four bits.
- switch (utf8 >> 4) {
- case 0x00:
- case 0x01:
- case 0x02:
- case 0x03:
- case 0x04:
- case 0x05:
- case 0x06:
- case 0x07:
- // Bit pattern 0xxx. No need for any extra bytes.
- break;
- case 0x08:
- case 0x09:
- case 0x0a:
- case 0x0b:
- case 0x0f:
- /*
- * Bit pattern 10xx or 1111, which are illegal start bytes.
- * Note: 1111 is valid for normal UTF-8, but not the
- * modified UTF-8 used here.
- */
- return false;
- case 0x0e:
- // Bit pattern 1110, so there are two additional bytes.
- utf8 = *(bytes++);
- if ((utf8 & 0xc0) != 0x80) {
- return false;
- }
- // Fall through to take care of the final byte.
- FALLTHROUGH_INTENDED;
- case 0x0c:
- case 0x0d:
- // Bit pattern 110x, so there is one additional byte.
- utf8 = *(bytes++);
- if ((utf8 & 0xc0) != 0x80) {
- return false;
- }
- break;
- }
- }
- return true;
-}
-
-class MyMediaScannerClient : public MediaScannerClient
-{
-public:
- MyMediaScannerClient(JNIEnv *env, jobject client)
- : mEnv(env),
- mClient(env->NewGlobalRef(client)),
- mScanFileMethodID(0),
- mHandleStringTagMethodID(0),
- mSetMimeTypeMethodID(0)
- {
- ALOGV("MyMediaScannerClient constructor");
- jclass mediaScannerClientInterface =
- env->FindClass(kClassMediaScannerClient);
-
- if (mediaScannerClientInterface == NULL) {
- ALOGE("Class %s not found", kClassMediaScannerClient);
- } else {
- mScanFileMethodID = env->GetMethodID(
- mediaScannerClientInterface,
- "scanFile",
- "(Ljava/lang/String;JJZZ)V");
-
- mHandleStringTagMethodID = env->GetMethodID(
- mediaScannerClientInterface,
- "handleStringTag",
- "(Ljava/lang/String;Ljava/lang/String;)V");
-
- mSetMimeTypeMethodID = env->GetMethodID(
- mediaScannerClientInterface,
- "setMimeType",
- "(Ljava/lang/String;)V");
- }
- }
-
- virtual ~MyMediaScannerClient()
- {
- ALOGV("MyMediaScannerClient destructor");
- mEnv->DeleteGlobalRef(mClient);
- }
-
- virtual status_t scanFile(const char* path, long long lastModified,
- long long fileSize, bool isDirectory, bool noMedia)
- {
- ALOGV("scanFile: path(%s), time(%lld), size(%lld) and isDir(%d)",
- path, lastModified, fileSize, isDirectory);
-
- jstring pathStr;
- if ((pathStr = mEnv->NewStringUTF(path)) == NULL) {
- mEnv->ExceptionClear();
- return NO_MEMORY;
- }
-
- mEnv->CallVoidMethod(mClient, mScanFileMethodID, pathStr, lastModified,
- fileSize, isDirectory, noMedia);
-
- mEnv->DeleteLocalRef(pathStr);
- return checkAndClearExceptionFromCallback(mEnv, "scanFile");
- }
-
- virtual status_t handleStringTag(const char* name, const char* value)
- {
- ALOGV("handleStringTag: name(%s) and value(%s)", name, value);
- jstring nameStr, valueStr;
- if ((nameStr = mEnv->NewStringUTF(name)) == NULL) {
- mEnv->ExceptionClear();
- return NO_MEMORY;
- }
- char *cleaned = NULL;
- if (!isValidUtf8(value)) {
- cleaned = strdup(value);
- char *chp = cleaned;
- char ch;
- while ((ch = *chp)) {
- if (ch & 0x80) {
- *chp = '?';
- }
- chp++;
- }
- value = cleaned;
- }
- valueStr = mEnv->NewStringUTF(value);
- free(cleaned);
- if (valueStr == NULL) {
- mEnv->DeleteLocalRef(nameStr);
- mEnv->ExceptionClear();
- return NO_MEMORY;
- }
-
- mEnv->CallVoidMethod(
- mClient, mHandleStringTagMethodID, nameStr, valueStr);
-
- mEnv->DeleteLocalRef(nameStr);
- mEnv->DeleteLocalRef(valueStr);
- return checkAndClearExceptionFromCallback(mEnv, "handleStringTag");
- }
-
- virtual status_t setMimeType(const char* mimeType)
- {
- ALOGV("setMimeType: %s", mimeType);
- jstring mimeTypeStr;
- if ((mimeTypeStr = mEnv->NewStringUTF(mimeType)) == NULL) {
- mEnv->ExceptionClear();
- return NO_MEMORY;
- }
-
- mEnv->CallVoidMethod(mClient, mSetMimeTypeMethodID, mimeTypeStr);
-
- mEnv->DeleteLocalRef(mimeTypeStr);
- return checkAndClearExceptionFromCallback(mEnv, "setMimeType");
- }
-
-private:
- JNIEnv *mEnv;
- jobject mClient;
- jmethodID mScanFileMethodID;
- jmethodID mHandleStringTagMethodID;
- jmethodID mSetMimeTypeMethodID;
-};
-
-
-static MediaScanner *getNativeScanner_l(JNIEnv* env, jobject thiz)
-{
- return (MediaScanner *) env->GetLongField(thiz, fields.context);
-}
-
-static void setNativeScanner_l(JNIEnv* env, jobject thiz, MediaScanner *s)
-{
- env->SetLongField(thiz, fields.context, (jlong)s);
-}
-
-static void
-android_media_MediaScanner_processDirectory(
- JNIEnv *env, jobject thiz, jstring path, jobject client)
-{
- ALOGV("processDirectory");
- MediaScanner *mp = getNativeScanner_l(env, thiz);
- if (mp == NULL) {
- jniThrowException(env, kRunTimeException, "No scanner available");
- return;
- }
-
- if (path == NULL) {
- jniThrowException(env, kIllegalArgumentException, NULL);
- return;
- }
-
- const char *pathStr = env->GetStringUTFChars(path, NULL);
- if (pathStr == NULL) { // Out of memory
- return;
- }
-
- MyMediaScannerClient myClient(env, client);
- MediaScanResult result = mp->processDirectory(pathStr, myClient);
- if (result == MEDIA_SCAN_RESULT_ERROR) {
- ALOGE("An error occurred while scanning directory '%s'.", pathStr);
- }
- env->ReleaseStringUTFChars(path, pathStr);
-}
-
-static jboolean
-android_media_MediaScanner_processFile(
- JNIEnv *env, jobject thiz, jstring path,
- jstring mimeType, jobject client)
-{
- ALOGV("processFile");
-
- // Lock already hold by processDirectory
- MediaScanner *mp = getNativeScanner_l(env, thiz);
- if (mp == NULL) {
- jniThrowException(env, kRunTimeException, "No scanner available");
- return false;
- }
-
- if (path == NULL) {
- jniThrowException(env, kIllegalArgumentException, NULL);
- return false;
- }
-
- const char *pathStr = env->GetStringUTFChars(path, NULL);
- if (pathStr == NULL) { // Out of memory
- return false;
- }
-
- const char *mimeTypeStr =
- (mimeType ? env->GetStringUTFChars(mimeType, NULL) : NULL);
- if (mimeType && mimeTypeStr == NULL) { // Out of memory
- // ReleaseStringUTFChars can be called with an exception pending.
- env->ReleaseStringUTFChars(path, pathStr);
- return false;
- }
-
- MyMediaScannerClient myClient(env, client);
- MediaScanResult result = mp->processFile(pathStr, mimeTypeStr, myClient);
- if (result == MEDIA_SCAN_RESULT_ERROR) {
- ALOGE("An error occurred while scanning file '%s'.", pathStr);
- }
- env->ReleaseStringUTFChars(path, pathStr);
- if (mimeType) {
- env->ReleaseStringUTFChars(mimeType, mimeTypeStr);
- }
- return result != MEDIA_SCAN_RESULT_ERROR;
-}
-
-static void
-android_media_MediaScanner_setLocale(
- JNIEnv *env, jobject thiz, jstring locale)
-{
- ALOGV("setLocale");
- MediaScanner *mp = getNativeScanner_l(env, thiz);
- if (mp == NULL) {
- jniThrowException(env, kRunTimeException, "No scanner available");
- return;
- }
-
- if (locale == NULL) {
- jniThrowException(env, kIllegalArgumentException, NULL);
- return;
- }
- const char *localeStr = env->GetStringUTFChars(locale, NULL);
- if (localeStr == NULL) { // Out of memory
- return;
- }
- mp->setLocale(localeStr);
-
- env->ReleaseStringUTFChars(locale, localeStr);
-}
-
-static jbyteArray
-android_media_MediaScanner_extractAlbumArt(
- JNIEnv *env, jobject thiz, jobject fileDescriptor)
-{
- ALOGV("extractAlbumArt");
- MediaScanner *mp = getNativeScanner_l(env, thiz);
- if (mp == NULL) {
- jniThrowException(env, kRunTimeException, "No scanner available");
- return NULL;
- }
-
- if (fileDescriptor == NULL) {
- jniThrowException(env, kIllegalArgumentException, NULL);
- return NULL;
- }
-
- int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
- MediaAlbumArt* mediaAlbumArt = mp->extractAlbumArt(fd);
- if (mediaAlbumArt == NULL) {
- return NULL;
- }
-
- jbyteArray array = env->NewByteArray(mediaAlbumArt->size());
- if (array != NULL) {
- const jbyte* data =
- reinterpret_cast<const jbyte*>(mediaAlbumArt->data());
- env->SetByteArrayRegion(array, 0, mediaAlbumArt->size(), data);
- }
-
- free(mediaAlbumArt);
- // if NewByteArray() returned NULL, an out-of-memory
- // exception will have been raised. I just want to
- // return null in that case.
- env->ExceptionClear();
- return array;
-}
-
-// This function gets a field ID, which in turn causes class initialization.
-// It is called from a static block in MediaScanner, which won't run until the
-// first time an instance of this class is used.
-static void
-android_media_MediaScanner_native_init(JNIEnv *env)
-{
- ALOGV("native_init");
- jclass clazz = env->FindClass(kClassMediaScanner);
- if (clazz == NULL) {
- return;
- }
-
- fields.context = env->GetFieldID(clazz, "mNativeContext", "J");
- if (fields.context == NULL) {
- return;
- }
-}
-
-static void
-android_media_MediaScanner_native_setup(JNIEnv *env, jobject thiz)
-{
- ALOGV("native_setup");
- MediaScanner *mp = new StagefrightMediaScanner;
-
- if (mp == NULL) {
- jniThrowException(env, kRunTimeException, "Out of memory");
- return;
- }
-
- env->SetLongField(thiz, fields.context, (jlong)mp);
-}
-
-static void
-android_media_MediaScanner_native_finalize(JNIEnv *env, jobject thiz)
-{
- ALOGV("native_finalize");
- MediaScanner *mp = getNativeScanner_l(env, thiz);
- if (mp == 0) {
- return;
- }
- delete mp;
- setNativeScanner_l(env, thiz, 0);
-}
-
-static const JNINativeMethod gMethods[] = {
- {
- "processDirectory",
- "(Ljava/lang/String;Landroid/media/MediaScannerClient;)V",
- (void *)android_media_MediaScanner_processDirectory
- },
-
- {
- "processFile",
- "(Ljava/lang/String;Ljava/lang/String;Landroid/media/MediaScannerClient;)Z",
- (void *)android_media_MediaScanner_processFile
- },
-
- {
- "setLocale",
- "(Ljava/lang/String;)V",
- (void *)android_media_MediaScanner_setLocale
- },
-
- {
- "extractAlbumArt",
- "(Ljava/io/FileDescriptor;)[B",
- (void *)android_media_MediaScanner_extractAlbumArt
- },
-
- {
- "native_init",
- "()V",
- (void *)android_media_MediaScanner_native_init
- },
-
- {
- "native_setup",
- "()V",
- (void *)android_media_MediaScanner_native_setup
- },
-
- {
- "native_finalize",
- "()V",
- (void *)android_media_MediaScanner_native_finalize
- },
-};
-
-// This function only registers the native methods, and is called from
-// JNI_OnLoad in android_media_MediaPlayer.cpp
-int register_android_media_MediaScanner(JNIEnv *env)
-{
- return AndroidRuntime::registerNativeMethods(env,
- kClassMediaScanner, gMethods, NELEM(gMethods));
-}