summaryrefslogtreecommitdiffstats
path: root/media/java
diff options
context:
space:
mode:
Diffstat (limited to 'media/java')
-rw-r--r--media/java/android/media/AudioTrack.java48
-rw-r--r--media/java/android/media/MediaFile.java7
-rw-r--r--media/java/android/media/MediaScanner.java81
-rw-r--r--media/java/android/mtp/MtpPropertyGroup.java1
-rw-r--r--media/java/android/mtp/MtpServer.java30
-rw-r--r--media/java/android/mtp/MtpStorage.java11
6 files changed, 115 insertions, 63 deletions
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index b97c3c4..b20a6e9 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -32,24 +32,25 @@ import android.util.Log;
* It allows to stream PCM audio buffers to the audio hardware for playback. This is
* achieved by "pushing" the data to the AudioTrack object using one of the
* {@link #write(byte[], int, int)} and {@link #write(short[], int, int)} methods.
- *
+ *
* <p>An AudioTrack instance can operate under two modes: static or streaming.<br>
* In Streaming mode, the application writes a continuous stream of data to the AudioTrack, using
- * one of the write() methods. These are blocking and return when the data has been transferred
- * from the Java layer to the native layer and queued for playback. The streaming mode
- * is most useful when playing blocks of audio data that for instance are:
+ * one of the {@code write()} methods. These are blocking and return when the data has been
+ * transferred from the Java layer to the native layer and queued for playback. The streaming
+ * mode is most useful when playing blocks of audio data that for instance are:
+ *
* <ul>
* <li>too big to fit in memory because of the duration of the sound to play,</li>
* <li>too big to fit in memory because of the characteristics of the audio data
* (high sampling rate, bits per sample ...)</li>
* <li>received or generated while previously queued audio is playing.</li>
* </ul>
+ *
* The static mode is to be chosen when dealing with short sounds that fit in memory and
- * that need to be played with the smallest latency possible. AudioTrack instances in static mode
- * can play the sound without the need to transfer the audio data from Java to native layer
- * each time the sound is to be played. The static mode will therefore be preferred for UI and
- * game sounds that are played often, and with the smallest overhead possible.
- *
+ * that need to be played with the smallest latency possible. The static mode will
+ * therefore be preferred for UI and game sounds that are played often, and with the
+ * smallest overhead possible.
+ *
* <p>Upon creation, an AudioTrack object initializes its associated audio buffer.
* The size of this buffer, specified during the construction, determines how long an AudioTrack
* can play before running out of data.<br>
@@ -816,6 +817,7 @@ public class AudioTrack
//--------------------
/**
* Starts playing an AudioTrack.
+ *
* @throws IllegalStateException
*/
public void play()
@@ -832,6 +834,7 @@ public class AudioTrack
/**
* Stops playing the audio data.
+ *
* @throws IllegalStateException
*/
public void stop()
@@ -848,7 +851,10 @@ public class AudioTrack
}
/**
- * Pauses the playback of the audio data.
+ * Pauses the playback of the audio data. Data that has not been played
+ * back will not be discarded. Subsequent calls to {@link #play} will play
+ * this data back.
+ *
* @throws IllegalStateException
*/
public void pause()
@@ -871,9 +877,9 @@ public class AudioTrack
//--------------------
/**
- * Flushes the audio data currently queued for playback.
+ * Flushes the audio data currently queued for playback. Any data that has
+ * not been played back will be discarded.
*/
-
public void flush() {
if (mState == STATE_INITIALIZED) {
// flush the data in native layer
@@ -883,9 +889,14 @@ public class AudioTrack
}
/**
- * Writes the audio data to the audio hardware for playback.
+ * Writes the audio data to the audio hardware for playback. Will block until
+ * all data has been written to the audio mixer.
+ * Note that the actual playback of this data might occur after this function
+ * returns. This function is thread safe with respect to {@link #stop} calls,
+ * in which case all of the specified data might not be written to the mixer.
+ *
* @param audioData the array that holds the data to play.
- * @param offsetInBytes the offset expressed in bytes in audioData where the data to play
+ * @param offsetInBytes the offset expressed in bytes in audioData where the data to play
* starts.
* @param sizeInBytes the number of bytes to read in audioData after the offset.
* @return the number of bytes that were written or {@link #ERROR_INVALID_OPERATION}
@@ -914,7 +925,12 @@ public class AudioTrack
/**
- * Writes the audio data to the audio hardware for playback.
+ * Writes the audio data to the audio hardware for playback. Will block until
+ * all data has been written to the audio mixer.
+ * Note that the actual playback of this data might occur after this function
+ * returns. This function is thread safe with respect to {@link #stop} calls,
+ * in which case all of the specified data might not be written to the mixer.
+ *
* @param audioData the array that holds the data to play.
* @param offsetInShorts the offset expressed in shorts in audioData where the data to play
* starts.
@@ -988,7 +1004,7 @@ public class AudioTrack
/**
* Sets the send level of the audio track to the attached auxiliary effect
- * {@see #attachAuxEffect(int)}. The level value range is 0 to 1.0.
+ * {@link #attachAuxEffect(int)}. The level value range is 0 to 1.0.
* <p>By default the send level is 0, so even if an effect is attached to the player
* this method must be called for the effect to be applied.
* <p>Note that the passed level value is a raw scalar. UI controls should be scaled
diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java
index 6df2f73..816d215 100644
--- a/media/java/android/media/MediaFile.java
+++ b/media/java/android/media/MediaFile.java
@@ -104,10 +104,9 @@ public class MediaFile {
public static final int FILE_TYPE_MS_POWERPOINT = 106;
public static final int FILE_TYPE_ZIP = 107;
- static class MediaFileType {
-
- int fileType;
- String mimeType;
+ public static class MediaFileType {
+ public final int fileType;
+ public final String mimeType;
MediaFileType(int fileType, String mimeType) {
this.fileType = fileType;
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index c55338a..e89be08 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -101,6 +101,8 @@ import java.util.Iterator;
* 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}
*/
public class MediaScanner
@@ -368,6 +370,34 @@ public class MediaScanner
}
}
+ private class FileInserter {
+
+ ContentValues[] mValues = new ContentValues[1000];
+ int mIndex = 0;
+
+ public Uri insert(ContentValues values) {
+ if (mIndex == mValues.length) {
+ flush();
+ }
+ mValues[mIndex++] = values;
+ // URI not needed when doing bulk inserts
+ return null;
+ }
+
+ public void flush() {
+ while (mIndex < mValues.length) {
+ mValues[mIndex++] = null;
+ }
+ try {
+ mMediaProvider.bulkInsert(mFilesUri, mValues);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in FileInserter.flush()", e);
+ }
+ mIndex = 0;
+ }
+ }
+ private FileInserter mFileInserter;
+
// hashes file path to FileCacheEntry.
// path should be lower case if mCaseInsensitivePaths is true
private HashMap<String, FileCacheEntry> mFileCache;
@@ -805,35 +835,42 @@ public class MediaScanner
}
}
+ // For inserts we always use the file URI so we can insert in bulk.
+ // For updates we compute the URI based on the media type.
Uri tableUri = mFilesUri;
- if (!mNoMedia) {
- if (MediaFile.isVideoFileType(mFileType)) {
- tableUri = mVideoUri;
- } else if (MediaFile.isImageFileType(mFileType)) {
- tableUri = mImagesUri;
- } else if (MediaFile.isAudioFileType(mFileType)) {
- tableUri = mAudioUri;
- }
- }
Uri result = null;
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);
+ int format = entry.mFormat;
+ if (format == 0) {
+ format = MediaFile.getFormatCode(entry.mPath, mMimeType);
}
+ values.put(Files.FileColumns.FORMAT, format);
+
// new file, insert it
- result = mMediaProvider.insert(tableUri, values);
+ if (mFileInserter != null) {
+ result = mFileInserter.insert(values);
+ } else {
+ result = mMediaProvider.insert(tableUri, values);
+ }
+
if (result != null) {
rowId = ContentUris.parseId(result);
entry.mRowId = rowId;
}
} else {
+ if (!mNoMedia) {
+ if (MediaFile.isVideoFileType(mFileType)) {
+ tableUri = mVideoUri;
+ } else if (MediaFile.isImageFileType(mFileType)) {
+ tableUri = mImagesUri;
+ } else if (MediaFile.isAudioFileType(mFileType)) {
+ tableUri = mAudioUri;
+ }
+ }
+
// updated file
result = ContentUris.withAppendedId(tableUri, rowId);
// path should never change, and we want to avoid replacing mixed cased paths
@@ -854,7 +891,7 @@ public class MediaScanner
new String[] { genre }, null);
if (cursor == null || cursor.getCount() == 0) {
// genre does not exist, so create the genre in the genre table
- values.clear();
+ values = new ContentValues();
values.put(MediaStore.Audio.Genres.NAME, genre);
uri = mMediaProvider.insert(mGenresUri, values);
} else {
@@ -876,7 +913,7 @@ public class MediaScanner
if (uri != null) {
// add entry to audio_genre_map
- values.clear();
+ values = new ContentValues();
values.put(MediaStore.Audio.Genres.Members.AUDIO_ID, Long.valueOf(rowId));
mMediaProvider.insert(uri, values);
}
@@ -1152,10 +1189,6 @@ public class MediaScanner
mPlaylistsUri = Playlists.getContentUri(volumeName);
mCaseInsensitivePaths = true;
- if (!Process.supportsProcesses()) {
- // Simulator uses host file system, so it should be case sensitive.
- mCaseInsensitivePaths = false;
- }
}
}
@@ -1165,10 +1198,14 @@ public class MediaScanner
initialize(volumeName);
prescan(null, true);
long prescan = System.currentTimeMillis();
+ mFileInserter = new FileInserter();
for (int i = 0; i < directories.length; i++) {
processDirectory(directories[i], mClient);
}
+ mFileInserter.flush();
+ mFileInserter = null;
+
long scan = System.currentTimeMillis();
postscan(directories);
long end = System.currentTimeMillis();
diff --git a/media/java/android/mtp/MtpPropertyGroup.java b/media/java/android/mtp/MtpPropertyGroup.java
index b75b11a..76c8569 100644
--- a/media/java/android/mtp/MtpPropertyGroup.java
+++ b/media/java/android/mtp/MtpPropertyGroup.java
@@ -330,7 +330,6 @@ class MtpPropertyGroup {
}
int count = (c == null ? 1 : c.getCount());
- Log.d(TAG, "count: " + count);
MtpPropertyList result = new MtpPropertyList(count * mProperties.length,
MtpConstants.RESPONSE_OK);
diff --git a/media/java/android/mtp/MtpServer.java b/media/java/android/mtp/MtpServer.java
index 0133cf6..f561cc0 100644
--- a/media/java/android/mtp/MtpServer.java
+++ b/media/java/android/mtp/MtpServer.java
@@ -16,18 +16,13 @@
package android.mtp;
-import android.util.Log;
-
/**
* Java wrapper for MTP/PTP support as USB responder.
* {@hide}
*/
-public class MtpServer {
-
- private final Object mLock = new Object();
- private boolean mStarted;
+public class MtpServer implements Runnable {
- private static final String TAG = "MtpServer";
+ private int mNativeContext; // accessed by native methods
static {
System.loadLibrary("media_jni");
@@ -38,19 +33,14 @@ public class MtpServer {
}
public void start() {
- synchronized (mLock) {
- native_start();
- mStarted = true;
- }
+ Thread thread = new Thread(this, "MtpServer");
+ thread.start();
}
- public void stop() {
- synchronized (mLock) {
- if (mStarted) {
- native_stop();
- mStarted = false;
- }
- }
+ @Override
+ public void run() {
+ native_run();
+ native_cleanup();
}
public void sendObjectAdded(int handle) {
@@ -70,8 +60,8 @@ public class MtpServer {
}
private native final void native_setup(MtpDatabase database, boolean usePtp);
- private native final void native_start();
- private native final void native_stop();
+ private native final void native_run();
+ private native final void native_cleanup();
private native final void native_send_object_added(int handle);
private native final void native_send_object_removed(int handle);
private native final void native_add_storage(MtpStorage storage);
diff --git a/media/java/android/mtp/MtpStorage.java b/media/java/android/mtp/MtpStorage.java
index 7932d34..da190a6 100644
--- a/media/java/android/mtp/MtpStorage.java
+++ b/media/java/android/mtp/MtpStorage.java
@@ -32,6 +32,7 @@ public class MtpStorage {
private final String mDescription;
private final long mReserveSpace;
private final boolean mRemovable;
+ private final long mMaxFileSize;
public MtpStorage(StorageVolume volume) {
mStorageId = volume.getStorageId();
@@ -39,6 +40,7 @@ public class MtpStorage {
mDescription = volume.getDescription();
mReserveSpace = volume.getMtpReserveSpace();
mRemovable = volume.isRemovable();
+ mMaxFileSize = volume.getMaxFileSize();
}
/**
@@ -98,4 +100,13 @@ public class MtpStorage {
public final boolean isRemovable() {
return mRemovable;
}
+
+ /**
+ * Returns maximum file size for the storage, or zero if it is unbounded.
+ *
+ * @return maximum file size
+ */
+ public long getMaxFileSize() {
+ return mMaxFileSize;
+ }
}