summaryrefslogtreecommitdiffstats
path: root/media/java
diff options
context:
space:
mode:
Diffstat (limited to 'media/java')
-rw-r--r--media/java/android/media/AudioManager.java21
-rw-r--r--media/java/android/media/AudioService.java136
-rw-r--r--media/java/android/media/DataSource.java43
-rw-r--r--media/java/android/media/MediaExtractor.java9
-rw-r--r--media/java/android/media/MediaFile.java2
-rw-r--r--media/java/android/media/MediaMetadataRetriever.java5
-rw-r--r--media/java/android/media/MediaPlayer.java26
-rw-r--r--media/java/android/media/MediaRecorder.java8
-rw-r--r--media/java/android/media/MediaScanner.java64
9 files changed, 249 insertions, 65 deletions
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index b6e4659..b5613f3 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -49,6 +49,7 @@ public class AudioManager {
private final Context mContext;
private long mVolumeKeyUpTime;
private final boolean mUseMasterVolume;
+ private final boolean mUseVolumeKeySounds;
private static String TAG = "AudioManager";
/**
@@ -412,6 +413,8 @@ public class AudioManager {
mContext = context;
mUseMasterVolume = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_useMasterVolume);
+ mUseVolumeKeySounds = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_useVolumeKeySounds);
}
private static IAudioService getService()
@@ -463,6 +466,7 @@ public class AudioManager {
* responsive to the user.
*/
int flags = FLAG_SHOW_UI | FLAG_VIBRATE;
+
if (mUseMasterVolume) {
adjustMasterVolume(
keyCode == KeyEvent.KEYCODE_VOLUME_UP
@@ -502,18 +506,17 @@ public class AudioManager {
* Play a sound. This is done on key up since we don't want the
* sound to play when a user holds down volume down to mute.
*/
- if (mUseMasterVolume) {
- if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
+ if (mUseVolumeKeySounds) {
+ if (mUseMasterVolume) {
adjustMasterVolume(ADJUST_SAME, FLAG_PLAY_SOUND);
+ } else {
+ int flags = FLAG_PLAY_SOUND;
+ adjustSuggestedStreamVolume(
+ ADJUST_SAME,
+ stream,
+ flags);
}
- } else {
- int flags = FLAG_PLAY_SOUND;
- adjustSuggestedStreamVolume(
- ADJUST_SAME,
- stream,
- flags);
}
-
mVolumeKeyUpTime = SystemClock.uptimeMillis();
break;
}
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 2e153dd..aea8a88 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -57,6 +57,7 @@ import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
+import android.os.UserHandle;
import android.os.Vibrator;
import android.provider.Settings;
import android.provider.Settings.System;
@@ -143,11 +144,12 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
private static final int MSG_REEVALUATE_REMOTE = 17;
private static final int MSG_RCC_NEW_PLAYBACK_INFO = 18;
private static final int MSG_RCC_NEW_VOLUME_OBS = 19;
+ private static final int MSG_SET_FORCE_BT_A2DP_USE = 20;
// start of messages handled under wakelock
// these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
// and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
- private static final int MSG_SET_WIRED_DEVICE_CONNECTION_STATE = 20;
- private static final int MSG_SET_A2DP_CONNECTION_STATE = 21;
+ private static final int MSG_SET_WIRED_DEVICE_CONNECTION_STATE = 21;
+ private static final int MSG_SET_A2DP_CONNECTION_STATE = 22;
// end of messages handled under wakelock
// flags for MSG_PERSIST_VOLUME indicating if current and/or last audible volume should be
@@ -381,7 +383,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
// message looper for SoundPool listener
private Looper mSoundPoolLooper = null;
// volume applied to sound played with playSoundEffect()
- private static int SOUND_EFFECT_VOLUME_DB;
+ private static int sSoundEffectVolumeDb;
// getActiveStreamType() will return STREAM_NOTIFICATION during this period after a notification
// stopped
private static final int NOTIFICATION_VOLUME_DELAY_MS = 5000;
@@ -438,7 +440,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
"ro.config.vc_call_vol_steps",
MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL]);
- SOUND_EFFECT_VOLUME_DB = context.getResources().getInteger(
+ sSoundEffectVolumeDb = context.getResources().getInteger(
com.android.internal.R.integer.config_soundEffectVolumeDb);
mVolumePanel = new VolumePanel(context, this);
@@ -929,6 +931,24 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
return delta;
}
+ private void sendBroadcastToAll(Intent intent) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private void sendStickyBroadcastToAll(Intent intent) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
// UI update and Broadcast Intent
private void sendVolumeUpdate(int streamType, int oldIndex, int index, int flags) {
if (!mVoiceCapable && (streamType == AudioSystem.STREAM_RING)) {
@@ -943,7 +963,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, streamType);
intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, index);
intent.putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, oldIndex);
- mContext.sendBroadcast(intent);
+ sendBroadcastToAll(intent);
}
// UI update and Broadcast Intent
@@ -953,7 +973,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
Intent intent = new Intent(AudioManager.MASTER_VOLUME_CHANGED_ACTION);
intent.putExtra(AudioManager.EXTRA_PREV_MASTER_VOLUME_VALUE, oldVolume);
intent.putExtra(AudioManager.EXTRA_MASTER_VOLUME_VALUE, newVolume);
- mContext.sendBroadcast(intent);
+ sendBroadcastToAll(intent);
}
// UI update and Broadcast Intent
@@ -967,9 +987,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
intent.putExtra(AudioManager.EXTRA_MASTER_VOLUME_MUTED, muted);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
| Intent.FLAG_RECEIVER_REPLACE_PENDING);
- long origCallerIdentityToken = Binder.clearCallingIdentity();
- mContext.sendStickyBroadcast(intent);
- Binder.restoreCallingIdentity(origCallerIdentityToken);
+ sendStickyBroadcastToAll(intent);
}
/**
@@ -1701,7 +1719,13 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
/** @see AudioManager#setBluetoothA2dpOn() */
public void setBluetoothA2dpOn(boolean on) {
- setBluetoothA2dpOnInt(on);
+ synchronized (mBluetoothA2dpEnabledLock) {
+ mBluetoothA2dpEnabled = on;
+ sendMsg(mAudioHandler, MSG_SET_FORCE_BT_A2DP_USE, SENDMSG_QUEUE,
+ AudioSystem.FOR_MEDIA,
+ mBluetoothA2dpEnabled ? AudioSystem.FORCE_NONE : AudioSystem.FORCE_NO_BT_A2DP,
+ null, 0);
+ }
}
/** @see AudioManager#isBluetoothA2dpOn() */
@@ -1985,7 +2009,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state);
newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_PREVIOUS_STATE,
mScoConnectionState);
- mContext.sendStickyBroadcast(newIntent);
+ sendStickyBroadcastToAll(newIntent);
mScoConnectionState = state;
}
}
@@ -2276,9 +2300,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
broadcast.putExtra(AudioManager.EXTRA_RINGER_MODE, ringerMode);
broadcast.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
| Intent.FLAG_RECEIVER_REPLACE_PENDING);
- long origCallerIdentityToken = Binder.clearCallingIdentity();
- mContext.sendStickyBroadcast(broadcast);
- Binder.restoreCallingIdentity(origCallerIdentityToken);
+ sendStickyBroadcastToAll(broadcast);
}
private void broadcastVibrateSetting(int vibrateType) {
@@ -2287,7 +2309,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
Intent broadcast = new Intent(AudioManager.VIBRATE_SETTING_CHANGED_ACTION);
broadcast.putExtra(AudioManager.EXTRA_VIBRATE_TYPE, vibrateType);
broadcast.putExtra(AudioManager.EXTRA_VIBRATE_SETTING, getVibrateSetting(vibrateType));
- mContext.sendBroadcast(broadcast);
+ sendBroadcastToAll(broadcast);
}
}
@@ -2875,7 +2897,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
float volFloat;
// use default if volume is not specified by caller
if (volume < 0) {
- volFloat = (float)Math.pow(10, SOUND_EFFECT_VOLUME_DB/20);
+ volFloat = (float)Math.pow(10, (float)sSoundEffectVolumeDb/20);
} else {
volFloat = (float) volume / 1000.0f;
}
@@ -3049,6 +3071,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
break;
case MSG_SET_FORCE_USE:
+ case MSG_SET_FORCE_BT_A2DP_USE:
setForceUse(msg.arg1, msg.arg2);
break;
@@ -3167,7 +3190,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
}
private void sendBecomingNoisyIntent() {
- mContext.sendBroadcast(new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY));
+ sendBroadcastToAll(new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY));
}
// must be called synchronized on mConnectedDevices
@@ -3286,7 +3309,9 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
// sent if none of these devices is connected.
int mBecomingNoisyIntentDevices =
AudioSystem.DEVICE_OUT_WIRED_HEADSET | AudioSystem.DEVICE_OUT_WIRED_HEADPHONE |
- AudioSystem.DEVICE_OUT_ALL_A2DP;
+ AudioSystem.DEVICE_OUT_ALL_A2DP | AudioSystem.DEVICE_OUT_AUX_DIGITAL |
+ AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET | AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET |
+ AudioSystem.DEVICE_OUT_ALL_USB;
// must be called before removing the device from mConnectedDevices
private int checkSendBecomingNoisyIntent(int device, int state) {
@@ -3356,7 +3381,12 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
}
}
- ActivityManagerNative.broadcastStickyIntent(intent, null);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.USER_ALL);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
private void onSetWiredDeviceConnectionState(int device, int state, String name)
@@ -3508,7 +3538,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
// AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, scoAudioState);
- mContext.sendStickyBroadcast(newIntent);
+ sendStickyBroadcastToAll(newIntent);
}
} else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
mBootCompleted = true;
@@ -3525,7 +3555,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE,
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
- mContext.sendStickyBroadcast(newIntent);
+ sendStickyBroadcastToAll(newIntent);
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if (adapter != null) {
@@ -3652,9 +3682,10 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
while(stackIterator.hasNext()) {
FocusStackEntry fse = stackIterator.next();
- pw.println(" source:" + fse.mSourceRef + " -- client: " + fse.mClientId
+ pw.println(" source:" + fse.mSourceRef + " -- client: " + fse.mClientId
+ " -- duration: " + fse.mFocusChangeType
- + " -- uid: " + fse.mCallingUid);
+ + " -- uid: " + fse.mCallingUid
+ + " -- stream: " + fse.mStreamType);
}
}
}
@@ -3915,8 +3946,13 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
mMediaEventWakeLock.acquire();
keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
}
- mContext.sendOrderedBroadcast(keyIntent, null, mKeyEventDone,
- mAudioHandler, Activity.RESULT_OK, null, null);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.sendOrderedBroadcastAsUser(keyIntent, UserHandle.ALL,
+ null, mKeyEventDone, mAudioHandler, Activity.RESULT_OK, null, null);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
/**
@@ -3949,8 +3985,14 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
if (needWakeLock) {
keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
}
- mContext.sendOrderedBroadcast(keyIntent, null, mKeyEventDone,
- mAudioHandler, Activity.RESULT_OK, null, null);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.sendOrderedBroadcastAsUser(keyIntent, UserHandle.ALL,
+ null, mKeyEventDone,
+ mAudioHandler, Activity.RESULT_OK, null, null);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
}
}
@@ -4014,7 +4056,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
startVoiceBasedInteractions(needWakeLock);
break;
case VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS:
- if (DEBUG_RC) Log.v(TAG, " send simulated key event");
+ if (DEBUG_RC) Log.v(TAG, " send simulated key event, wakelock=" + needWakeLock);
sendSimulatedMediaButtonEvent(keyEvent, needWakeLock);
break;
}
@@ -4646,17 +4688,40 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
clearRemoteControlDisplay_syncAfRcs();
return;
}
- // if the top of the two stacks belong to different packages, there is a mismatch, clear
+
+ // determine which entry in the AudioFocus stack to consider, and compare against the
+ // top of the stack for the media button event receivers : simply using the top of the
+ // stack would make the entry disappear from the RemoteControlDisplay in conditions such as
+ // notifications playing during music playback.
+ // crawl the AudioFocus stack until an entry is found with the following characteristics:
+ // - focus gain on STREAM_MUSIC stream
+ // - non-transient focus gain on a stream other than music
+ FocusStackEntry af = null;
+ Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
+ while(stackIterator.hasNext()) {
+ FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
+ if ((fse.mStreamType == AudioManager.STREAM_MUSIC)
+ || (fse.mFocusChangeType == AudioManager.AUDIOFOCUS_GAIN)) {
+ af = fse;
+ break;
+ }
+ }
+ if (af == null) {
+ clearRemoteControlDisplay_syncAfRcs();
+ return;
+ }
+
+ // if the audio focus and RC owners belong to different packages, there is a mismatch, clear
if ((mRCStack.peek().mCallingPackageName != null)
- && (mFocusStack.peek().mPackageName != null)
+ && (af.mPackageName != null)
&& !(mRCStack.peek().mCallingPackageName.compareTo(
- mFocusStack.peek().mPackageName) == 0)) {
+ af.mPackageName) == 0)) {
clearRemoteControlDisplay_syncAfRcs();
return;
}
// if the audio focus didn't originate from the same Uid as the one in which the remote
// control information will be retrieved, clear
- if (mRCStack.peek().mCallingUid != mFocusStack.peek().mCallingUid) {
+ if (mRCStack.peek().mCallingUid != af.mCallingUid) {
clearRemoteControlDisplay_syncAfRcs();
return;
}
@@ -5290,10 +5355,9 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
public void setBluetoothA2dpOnInt(boolean on) {
synchronized (mBluetoothA2dpEnabledLock) {
mBluetoothA2dpEnabled = on;
- sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE,
- AudioSystem.FOR_MEDIA,
- mBluetoothA2dpEnabled ? AudioSystem.FORCE_NONE : AudioSystem.FORCE_NO_BT_A2DP,
- null, 0);
+ mAudioHandler.removeMessages(MSG_SET_FORCE_BT_A2DP_USE);
+ AudioSystem.setForceUse(AudioSystem.FOR_MEDIA,
+ mBluetoothA2dpEnabled ? AudioSystem.FORCE_NONE : AudioSystem.FORCE_NO_BT_A2DP);
}
}
diff --git a/media/java/android/media/DataSource.java b/media/java/android/media/DataSource.java
new file mode 100644
index 0000000..347bd5f
--- /dev/null
+++ b/media/java/android/media/DataSource.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+package android.media;
+
+import java.io.Closeable;
+
+/**
+ * An abstraction for a media data source, e.g. a file or an http stream
+ * {@hide}
+ */
+public interface DataSource extends Closeable {
+ /**
+ * Reads data from the data source at the requested position
+ *
+ * @param offset where in the source to read
+ * @param buffer the buffer to read the data into
+ * @param size how many bytes to read
+ * @return the number of bytes read, or -1 if there was an error
+ */
+ public int readAt(long offset, byte[] buffer, int size);
+
+ /**
+ * Gets the size of the data source.
+ *
+ * @return size of data source, or -1 if the length is unknown
+ */
+ public long getSize();
+}
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index 687d3a5..749ef12 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -22,6 +22,7 @@ import android.content.res.AssetFileDescriptor;
import android.media.MediaCodec;
import android.media.MediaFormat;
import android.net.Uri;
+
import java.io.FileDescriptor;
import java.io.IOException;
import java.nio.ByteBuffer;
@@ -44,7 +45,7 @@ import java.util.Map;
* }
* ByteBuffer inputBuffer = ByteBuffer.allocate(...)
* while (extractor.readSampleData(inputBuffer, ...) &gt;= 0) {
- * int trackIndex = extractor.getTrackIndex();
+ * int trackIndex = extractor.getSampleTrackIndex();
* long presentationTimeUs = extractor.getSampleTime();
* ...
* extractor.advance();
@@ -60,6 +61,12 @@ final public class MediaExtractor {
}
/**
+ * Sets the DataSource object to be used as the data source for this extractor
+ * {@hide}
+ */
+ public native final void setDataSource(DataSource source);
+
+ /**
* Sets the data source as a content Uri.
*
* @param context the Context to use when resolving the Uri
diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java
index d21ada4..06d43a2 100644
--- a/media/java/android/media/MediaFile.java
+++ b/media/java/android/media/MediaFile.java
@@ -325,7 +325,7 @@ public class MediaFile {
}
int lastDot = fileName.lastIndexOf('.');
if (lastDot > 0) {
- String extension = fileName.substring(lastDot + 1);
+ String extension = fileName.substring(lastDot + 1).toUpperCase();
Integer value = sFileTypeToFormatMap.get(extension);
if (value != null) {
return value.intValue();
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java
index aef631f..cc59d02 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -483,5 +483,10 @@ public class MediaMetadataRetriever
* of 180 degrees will be retrieved as "-90.0000+180.0000", for instance.
*/
public static final int METADATA_KEY_LOCATION = 23;
+ /**
+ * This key retrieves the video rotation angle in degrees, if available.
+ * The video rotation angle may be 0, 90, 180, or 270 degrees.
+ */
+ public static final int METADATA_KEY_VIDEO_ROTATION = 24;
// Add more here...
}
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index cd25865..ef0da3a 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -2265,6 +2265,16 @@ public class MediaPlayer
*/
public static final int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200;
+ /** File or network related operation errors. */
+ public static final int MEDIA_ERROR_IO = -1004;
+ /** Bitstream is not conforming to the related coding standard or file spec. */
+ public static final int MEDIA_ERROR_MALFORMED = -1007;
+ /** Bitstream is conforming to the related coding standard or file spec, but
+ * the media framework does not support the feature. */
+ public static final int MEDIA_ERROR_UNSUPPORTED = -1010;
+ /** Some operation takes too long to complete, usually more than 3-5 seconds. */
+ public static final int MEDIA_ERROR_TIMED_OUT = -110;
+
/**
* Interface definition of a callback to be invoked when there
* has been an error during an asynchronous operation (other errors
@@ -2282,7 +2292,13 @@ public class MediaPlayer
* <li>{@link #MEDIA_ERROR_SERVER_DIED}
* </ul>
* @param extra an extra code, specific to the error. Typically
- * implementation dependant.
+ * implementation dependent.
+ * <ul>
+ * <li>{@link #MEDIA_ERROR_IO}
+ * <li>{@link #MEDIA_ERROR_MALFORMED}
+ * <li>{@link #MEDIA_ERROR_UNSUPPORTED}
+ * <li>{@link #MEDIA_ERROR_TIMED_OUT}
+ * </ul>
* @return True if the method handled the error, false if it didn't.
* Returning false, or not having an OnErrorListener at all, will
* cause the OnCompletionListener to be called.
@@ -2319,6 +2335,11 @@ public class MediaPlayer
*/
public static final int MEDIA_INFO_STARTED_AS_NEXT = 2;
+ /** The player just pushed the very first video frame for rendering.
+ * @see android.media.MediaPlayer.OnInfoListener
+ */
+ public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3;
+
/** The video is too complex for the decoder: it can't decode frames fast
* enough. Possibly only the audio plays fine at this stage.
* @see android.media.MediaPlayer.OnInfoListener
@@ -2374,6 +2395,7 @@ public class MediaPlayer
* <ul>
* <li>{@link #MEDIA_INFO_UNKNOWN}
* <li>{@link #MEDIA_INFO_VIDEO_TRACK_LAGGING}
+ * <li>{@link #MEDIA_INFO_VIDEO_RENDERING_START}
* <li>{@link #MEDIA_INFO_BUFFERING_START}
* <li>{@link #MEDIA_INFO_BUFFERING_END}
* <li>{@link #MEDIA_INFO_BAD_INTERLEAVING}
@@ -2381,7 +2403,7 @@ public class MediaPlayer
* <li>{@link #MEDIA_INFO_METADATA_UPDATE}
* </ul>
* @param extra an extra code, specific to the info. Typically
- * implementation dependant.
+ * implementation dependent.
* @return True if the method handled the info, false if it didn't.
* Returning false, or not having an OnErrorListener at all, will
* cause the info to be discarded.
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 613354f..2d1be02 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -720,12 +720,17 @@ public class MediaRecorder
public native int getMaxAmplitude() throws IllegalStateException;
/* Do not change this value without updating its counterpart
- * in include/media/mediarecorder.h!
+ * in include/media/mediarecorder.h or mediaplayer.h!
*/
/** Unspecified media recorder error.
* @see android.media.MediaRecorder.OnErrorListener
*/
public static final int MEDIA_RECORDER_ERROR_UNKNOWN = 1;
+ /** Media server died. In this case, the application must release the
+ * MediaRecorder object and instantiate a new one.
+ * @see android.media.MediaRecorder.OnErrorListener
+ */
+ public static final int MEDIA_ERROR_SERVER_DIED = 100;
/**
* Interface definition for a callback to be invoked when an error
@@ -740,6 +745,7 @@ public class MediaRecorder
* @param what the type of error that has occurred:
* <ul>
* <li>{@link #MEDIA_RECORDER_ERROR_UNKNOWN}
+ * <li>{@link #MEDIA_ERROR_SERVER_DIED}
* </ul>
* @param extra an extra code, specific to the error type
*/
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index fd37bcf..88cf4ac 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -314,6 +314,7 @@ public class MediaScanner
private int mMtpObjectHandle;
private final String mExternalStoragePath;
+ private final boolean mExternalIsEmulated;
/** whether to use bulk inserts or individual inserts for each item */
private static final boolean ENABLE_BULK_INSERTS = true;
@@ -392,6 +393,7 @@ public class MediaScanner
setDefaultRingtoneFileNames();
mExternalStoragePath = Environment.getExternalStorageDirectory().getAbsolutePath();
+ mExternalIsEmulated = Environment.isExternalStorageEmulated();
//mClient.testGenreNameConverter();
}
@@ -543,13 +545,28 @@ public class MediaScanner
boolean music = (lowpath.indexOf(MUSIC_DIR) > 0) ||
(!ringtones && !notifications && !alarms && !podcasts);
+ boolean isaudio = MediaFile.isAudioFileType(mFileType);
+ boolean isvideo = MediaFile.isVideoFileType(mFileType);
+ boolean isimage = MediaFile.isImageFileType(mFileType);
+
+ if (isaudio || isvideo || isimage) {
+ if (mExternalIsEmulated && path.startsWith(mExternalStoragePath)) {
+ // try to rewrite the path to bypass the sd card fuse layer
+ String directPath = Environment.getMediaStorageDirectory() +
+ path.substring(mExternalStoragePath.length());
+ File f = new File(directPath);
+ if (f.exists()) {
+ path = directPath;
+ }
+ }
+ }
+
// we only extract metadata for audio and video files
- if (MediaFile.isAudioFileType(mFileType)
- || MediaFile.isVideoFileType(mFileType)) {
+ if (isaudio || isvideo) {
processFile(path, mimeType, this);
}
- if (MediaFile.isImageFileType(mFileType)) {
+ if (isimage) {
processImageFile(path);
}
@@ -972,7 +989,6 @@ public class MediaScanner
}
values.put(FileColumns.MEDIA_TYPE, mediaType);
}
-
mMediaProvider.update(result, values, null, null);
}
@@ -1448,24 +1464,42 @@ public class MediaScanner
}
FileEntry makeEntryFor(String path) {
- String key = path;
String where;
String[] selectionArgs;
- if (mCaseInsensitivePaths) {
- // the 'like' makes it use the index, the 'lower()' makes it correct
- // when the path contains sqlite wildcard characters
- where = "_data LIKE ?1 AND lower(_data)=lower(?1)";
- selectionArgs = new String[] { path };
- } else {
- where = Files.FileColumns.DATA + "=?";
- selectionArgs = new String[] { path };
- }
Cursor c = null;
try {
+ boolean hasWildCards = path.contains("_") || path.contains("%");
+
+ if (hasWildCards || !mCaseInsensitivePaths) {
+ // if there are wildcard characters in the path, the "like" match
+ // will be slow, and it's worth trying an "=" comparison
+ // first, since in most cases the case will match.
+ // Also, we shouldn't do a "like" match on case-sensitive filesystems
+ where = Files.FileColumns.DATA + "=?";
+ selectionArgs = new String[] { path };
+ } else {
+ // if there are no wildcard characters in the path, then the "like"
+ // match will be just as fast as the "=" case, because of the index
+ where = "_data LIKE ?1 AND lower(_data)=lower(?1)";
+ selectionArgs = new String[] { path };
+ }
c = mMediaProvider.query(mFilesUri, FILES_PRESCAN_PROJECTION,
where, selectionArgs, null, null);
- if (c.moveToNext()) {
+ if (!c.moveToFirst() && hasWildCards && mCaseInsensitivePaths) {
+ // Try again with case-insensitive match. This will be slower, especially
+ // if the path contains wildcard characters.
+ // The 'like' makes it use the index, the 'lower()' makes it correct
+ // when the path contains sqlite wildcard characters,
+ where = "_data LIKE ?1 AND lower(_data)=lower(?1)";
+ selectionArgs = new String[] { path };
+ c.close();
+ c = mMediaProvider.query(mFilesUri, FILES_PRESCAN_PROJECTION,
+ where, selectionArgs, null, null);
+ // TODO update the path in the db with the correct case so the fast
+ // path works next time?
+ }
+ if (c.moveToFirst()) {
long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX);
int format = c.getInt(FILES_PRESCAN_FORMAT_COLUMN_INDEX);
long lastModified = c.getLong(FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);