summaryrefslogtreecommitdiffstats
path: root/media/java
diff options
context:
space:
mode:
Diffstat (limited to 'media/java')
-rw-r--r--media/java/android/media/AmrInputStream.java2
-rw-r--r--media/java/android/media/AsyncPlayer.java2
-rw-r--r--media/java/android/media/AudioFormat.java5
-rw-r--r--media/java/android/media/AudioManager.java53
-rw-r--r--media/java/android/media/AudioRecord.java2
-rw-r--r--media/java/android/media/AudioService.java682
-rw-r--r--media/java/android/media/AudioSystem.java73
-rw-r--r--media/java/android/media/AudioTrack.java111
-rw-r--r--media/java/android/media/ExifInterface.java2
-rw-r--r--media/java/android/media/MediaFile.java10
-rw-r--r--media/java/android/media/MediaInserter.java2
-rw-r--r--media/java/android/media/MediaPlayer.java3
-rw-r--r--media/java/android/media/MediaRecorder.java21
-rw-r--r--media/java/android/media/MediaScanner.java142
-rw-r--r--media/java/android/media/MediaScannerConnection.java2
-rw-r--r--media/java/android/media/MiniThumbFile.java2
-rw-r--r--media/java/android/media/RemoteControlClient.java3
-rw-r--r--media/java/android/media/RingtoneManager.java2
-rw-r--r--media/java/android/media/SoundPool.java10
-rw-r--r--media/java/android/media/ThumbnailUtils.java17
-rw-r--r--media/java/android/media/audiofx/AudioEffect.java2
-rwxr-xr-xmedia/java/android/mtp/MtpDatabase.java46
-rw-r--r--media/java/android/mtp/MtpPropertyGroup.java10
23 files changed, 792 insertions, 412 deletions
diff --git a/media/java/android/media/AmrInputStream.java b/media/java/android/media/AmrInputStream.java
index bc68472..8b7eee2 100644
--- a/media/java/android/media/AmrInputStream.java
+++ b/media/java/android/media/AmrInputStream.java
@@ -44,7 +44,7 @@ public final class AmrInputStream extends InputStream
private int mGae;
// result amr stream
- private byte[] mBuf = new byte[SAMPLES_PER_FRAME * 2];
+ private final byte[] mBuf = new byte[SAMPLES_PER_FRAME * 2];
private int mBufIn = 0;
private int mBufOut = 0;
diff --git a/media/java/android/media/AsyncPlayer.java b/media/java/android/media/AsyncPlayer.java
index 09aec2e..804528e 100644
--- a/media/java/android/media/AsyncPlayer.java
+++ b/media/java/android/media/AsyncPlayer.java
@@ -49,7 +49,7 @@ public class AsyncPlayer {
}
}
- private LinkedList<Command> mCmdQueue = new LinkedList();
+ private final LinkedList<Command> mCmdQueue = new LinkedList();
private void startSound(Command cmd) {
// Preparing can be slow, so if there is something else
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 8990fe5..49f498e 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -31,10 +31,11 @@ public class AudioFormat {
public static final int ENCODING_INVALID = 0;
/** Default audio data format */
public static final int ENCODING_DEFAULT = 1;
+ // These two values must be kept in sync with JNI code for AudioTrack, AudioRecord
/** Audio data format: PCM 16 bit per sample. Guaranteed to be supported by devices. */
- public static final int ENCODING_PCM_16BIT = 2; // accessed by native code
+ public static final int ENCODING_PCM_16BIT = 2;
/** Audio data format: PCM 8 bit per sample. Not guaranteed to be supported by devices. */
- public static final int ENCODING_PCM_8BIT = 3; // accessed by native code
+ public static final int ENCODING_PCM_8BIT = 3;
/** Invalid audio channel configuration */
/** @deprecated use CHANNEL_INVALID instead */
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index a0881a7..78eb89f 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -22,8 +22,6 @@ import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.database.ContentObserver;
-import android.graphics.Bitmap;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
@@ -37,7 +35,6 @@ import android.util.Log;
import android.view.KeyEvent;
import android.view.VolumePanel;
-import java.util.Iterator;
import java.util.HashMap;
/**
@@ -49,11 +46,9 @@ import java.util.HashMap;
public class AudioManager {
private final Context mContext;
- private final Handler mHandler;
private long mVolumeKeyUpTime;
private int mVolumeControlStream = -1;
private static String TAG = "AudioManager";
- private static boolean localLOGV = false;
/**
* Broadcast intent, a hint for applications that audio is about to become
@@ -359,7 +354,6 @@ public class AudioManager {
*/
public AudioManager(Context context) {
mContext = context;
- mHandler = new Handler(context.getMainLooper());
}
private static IAudioService getService()
@@ -1515,7 +1509,7 @@ public class AudioManager {
* Map to convert focus event listener IDs, as used in the AudioService audio focus stack,
* to actual listener objects.
*/
- private HashMap<String, OnAudioFocusChangeListener> mAudioFocusIdListenerMap =
+ private final HashMap<String, OnAudioFocusChangeListener> mAudioFocusIdListenerMap =
new HashMap<String, OnAudioFocusChangeListener>();
/**
* Lock to prevent concurrent changes to the list of focus listeners for this AudioManager
@@ -1530,7 +1524,7 @@ public class AudioManager {
/**
* Handler for audio focus events coming from the audio service.
*/
- private FocusEventHandlerDelegate mAudioFocusEventHandlerDelegate =
+ private final FocusEventHandlerDelegate mAudioFocusEventHandlerDelegate =
new FocusEventHandlerDelegate();
/**
@@ -1569,7 +1563,7 @@ public class AudioManager {
}
}
- private IAudioFocusDispatcher mAudioFocusDispatcher = new IAudioFocusDispatcher.Stub() {
+ private final IAudioFocusDispatcher mAudioFocusDispatcher = new IAudioFocusDispatcher.Stub() {
public void dispatchAudioFocusChange(int focusChange, String id) {
Message m = mAudioFocusEventHandlerDelegate.getHandler().obtainMessage(focusChange, id);
@@ -1655,11 +1649,46 @@ public class AudioManager {
mAudioFocusDispatcher, getIdForAudioFocusListener(l),
mContext.getPackageName() /* package name */);
} catch (RemoteException e) {
- Log.e(TAG, "Can't call requestAudioFocus() from AudioService due to "+e);
+ Log.e(TAG, "Can't call requestAudioFocus() on AudioService due to "+e);
}
return status;
}
+ /**
+ * @hide
+ * Used internally by telephony package to request audio focus. Will cause the focus request
+ * to be associated with the "voice communication" identifier only used in AudioService
+ * to identify this use case.
+ * @param streamType use STREAM_RING for focus requests when ringing, VOICE_CALL for
+ * the establishment of the call
+ * @param durationHint the type of focus request. AUDIOFOCUS_GAIN_TRANSIENT is recommended so
+ * media applications resume after a call
+ */
+ public void requestAudioFocusForCall(int streamType, int durationHint) {
+ IAudioService service = getService();
+ try {
+ service.requestAudioFocus(streamType, durationHint, mICallBack, null,
+ AudioService.IN_VOICE_COMM_FOCUS_ID,
+ "system" /* dump-friendly package name */);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Can't call requestAudioFocusForCall() on AudioService due to "+e);
+ }
+ }
+
+ /**
+ * @hide
+ * Used internally by telephony package to abandon audio focus, typically after a call or
+ * when ringing ends and the call is rejected or not answered.
+ * Should match one or more calls to {@link #requestAudioFocusForCall(int, int)}.
+ */
+ public void abandonAudioFocusForCall() {
+ IAudioService service = getService();
+ try {
+ service.abandonAudioFocus(null, AudioService.IN_VOICE_COMM_FOCUS_ID);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Can't call abandonAudioFocusForCall() on AudioService due to "+e);
+ }
+ }
/**
* Abandon audio focus. Causes the previous focus owner, if any, to receive focus.
@@ -1674,7 +1703,7 @@ public class AudioManager {
status = service.abandonAudioFocus(mAudioFocusDispatcher,
getIdForAudioFocusListener(l));
} catch (RemoteException e) {
- Log.e(TAG, "Can't call abandonAudioFocus() from AudioService due to "+e);
+ Log.e(TAG, "Can't call abandonAudioFocus() on AudioService due to "+e);
}
return status;
}
@@ -1926,7 +1955,7 @@ public class AudioManager {
/**
* {@hide}
*/
- private IBinder mICallBack = new Binder();
+ private final IBinder mICallBack = new Binder();
/**
* Checks whether the phone is in silent mode, with or without vibrate.
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 855e831..5cc24c0 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -161,7 +161,7 @@ public class AudioRecord
/**
* Lock to make sure mRecordingState updates are reflecting the actual state of the object.
*/
- private Object mRecordingStateLock = new Object();
+ private final Object mRecordingStateLock = new Object();
/**
* The listener the AudioRecord notifies when the record position reaches a marker
* or for periodic updates during the progression of the record head.
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 37aacab..13e3982 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -102,8 +102,6 @@ public class AudioService extends IAudioService.Stub {
private VolumePanel mVolumePanel;
// sendMsg() flags
- /** Used when a message should be shared across all stream types. */
- private static final int SHARED_MSG = -1;
/** If the msg is already queued, replace it with this one. */
private static final int SENDMSG_REPLACE = 0;
/** If the msg is already queued, ignore this one and leave the old. */
@@ -112,7 +110,7 @@ public class AudioService extends IAudioService.Stub {
private static final int SENDMSG_QUEUE = 2;
// AudioHandler message.whats
- private static final int MSG_SET_SYSTEM_VOLUME = 0;
+ private static final int MSG_SET_DEVICE_VOLUME = 0;
private static final int MSG_PERSIST_VOLUME = 1;
private static final int MSG_PERSIST_RINGER_MODE = 3;
private static final int MSG_PERSIST_VIBRATE_SETTING = 4;
@@ -126,6 +124,13 @@ public class AudioService extends IAudioService.Stub {
private static final int MSG_BT_HEADSET_CNCT_FAILED = 12;
private static final int MSG_RCDISPLAY_CLEAR = 13;
private static final int MSG_RCDISPLAY_UPDATE = 14;
+ private static final int MSG_SET_ALL_VOLUMES = 15;
+
+
+ // flags for MSG_PERSIST_VOLUME indicating if current and/or last audible volume should be
+ // persisted
+ private static final int PERSIST_CURRENT = 0x1;
+ private static final int PERSIST_LAST_AUDIBLE = 0x2;
private static final int BTA2DP_DOCK_TIMEOUT_MILLIS = 8000;
// Timeout for connection to bluetooth headset service
@@ -141,11 +146,12 @@ public class AudioService extends IAudioService.Stub {
private SettingsObserver mSettingsObserver;
private int mMode;
- private Object mSettingsLock = new Object();
+ // protects mRingerMode
+ private final Object mSettingsLock = new Object();
private boolean mMediaServerOk;
private SoundPool mSoundPool;
- private Object mSoundEffectsLock = new Object();
+ private final Object mSoundEffectsLock = new Object();
private static final int NUM_SOUNDPOOL_CHANNELS = 4;
private static final int SOUND_EFFECT_VOLUME = 1000;
@@ -162,7 +168,7 @@ public class AudioService extends IAudioService.Stub {
/* Sound effect file name mapping sound effect id (AudioManager.FX_xxx) to
* file index in SOUND_EFFECT_FILES[] (first column) and indicating if effect
* uses soundpool (second column) */
- private int[][] SOUND_EFFECT_FILES_MAP = new int[][] {
+ private final int[][] SOUND_EFFECT_FILES_MAP = new int[][] {
{0, -1}, // FX_KEY_CLICK
{0, -1}, // FX_FOCUS_NAVIGATION_UP
{0, -1}, // FX_FOCUS_NAVIGATION_DOWN
@@ -175,7 +181,7 @@ public class AudioService extends IAudioService.Stub {
};
/** @hide Maximum volume index values for audio streams */
- private int[] MAX_STREAM_VOLUME = new int[] {
+ private final int[] MAX_STREAM_VOLUME = new int[] {
5, // STREAM_VOICE_CALL
7, // STREAM_SYSTEM
7, // STREAM_RING
@@ -191,7 +197,7 @@ public class AudioService extends IAudioService.Stub {
* of another stream: This avoids multiplying the volume settings for hidden
* stream types that follow other stream behavior for volume settings
* NOTE: do not create loops in aliases! */
- private int[] STREAM_VOLUME_ALIAS = new int[] {
+ private final int[] STREAM_VOLUME_ALIAS = new int[] {
AudioSystem.STREAM_VOICE_CALL, // STREAM_VOICE_CALL
AudioSystem.STREAM_SYSTEM, // STREAM_SYSTEM
AudioSystem.STREAM_RING, // STREAM_RING
@@ -204,19 +210,19 @@ public class AudioService extends IAudioService.Stub {
AudioSystem.STREAM_MUSIC // STREAM_TTS
};
- private AudioSystem.ErrorCallback mAudioSystemCallback = new AudioSystem.ErrorCallback() {
+ private final AudioSystem.ErrorCallback mAudioSystemCallback = new AudioSystem.ErrorCallback() {
public void onError(int error) {
switch (error) {
case AudioSystem.AUDIO_STATUS_SERVER_DIED:
if (mMediaServerOk) {
- sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SHARED_MSG, SENDMSG_NOOP, 0, 0,
+ sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SENDMSG_NOOP, 0, 0,
null, 1500);
mMediaServerOk = false;
}
break;
case AudioSystem.AUDIO_STATUS_OK:
if (!mMediaServerOk) {
- sendMsg(mAudioHandler, MSG_MEDIA_SERVER_STARTED, SHARED_MSG, SENDMSG_NOOP, 0, 0,
+ sendMsg(mAudioHandler, MSG_MEDIA_SERVER_STARTED, SENDMSG_NOOP, 0, 0,
null, 0);
mMediaServerOk = true;
}
@@ -232,6 +238,7 @@ public class AudioService extends IAudioService.Stub {
* {@link AudioManager#RINGER_MODE_SILENT}, or
* {@link AudioManager#RINGER_MODE_VIBRATE}.
*/
+ // protected by mSettingsLock
private int mRingerMode;
/** @see System#MODE_RINGER_STREAMS_AFFECTED */
@@ -263,17 +270,17 @@ public class AudioService extends IAudioService.Stub {
private boolean mIsRinging = false;
// Devices currently connected
- private HashMap <Integer, String> mConnectedDevices = new HashMap <Integer, String>();
+ private final HashMap <Integer, String> mConnectedDevices = new HashMap <Integer, String>();
// Forced device usage for communications
private int mForcedUseForComm;
// List of binder death handlers for setMode() client processes.
// The last process to have called setMode() is at the top of the list.
- private ArrayList <SetModeDeathHandler> mSetModeDeathHandlers = new ArrayList <SetModeDeathHandler>();
+ private final ArrayList <SetModeDeathHandler> mSetModeDeathHandlers = new ArrayList <SetModeDeathHandler>();
// List of clients having issued a SCO start request
- private ArrayList <ScoClient> mScoClients = new ArrayList <ScoClient>();
+ private final ArrayList <ScoClient> mScoClients = new ArrayList <ScoClient>();
// BluetoothHeadset API to control SCO connection
private BluetoothHeadset mBluetoothHeadset;
@@ -323,6 +330,7 @@ public class AudioService extends IAudioService.Stub {
// Keyguard manager proxy
private KeyguardManager mKeyguardManager;
+
///////////////////////////////////////////////////////////////////////////
// Construction
///////////////////////////////////////////////////////////////////////////
@@ -425,12 +433,17 @@ public class AudioService extends IAudioService.Stub {
// Correct stream index values for streams with aliases
for (int i = 0; i < numStreamTypes; i++) {
+ int device = getDeviceForStream(i);
if (STREAM_VOLUME_ALIAS[i] != i) {
- int index = rescaleIndex(streams[i].mIndex, STREAM_VOLUME_ALIAS[i], i);
- streams[i].mIndex = streams[i].getValidIndex(index);
- setStreamVolumeIndex(i, index);
- index = rescaleIndex(streams[i].mLastAudibleIndex, STREAM_VOLUME_ALIAS[i], i);
- streams[i].mLastAudibleIndex = streams[i].getValidIndex(index);
+ int index = rescaleIndex(streams[i].getIndex(device, false /* lastAudible */),
+ STREAM_VOLUME_ALIAS[i],
+ i);
+ streams[i].mIndex.put(device, streams[i].getValidIndex(index));
+ streams[i].applyDeviceVolume(device);
+ index = rescaleIndex(streams[i].getIndex(device, true /* lastAudible */),
+ STREAM_VOLUME_ALIAS[i],
+ i);
+ streams[i].mLastAudibleIndex.put(device, streams[i].getValidIndex(index));
}
}
}
@@ -438,12 +451,15 @@ public class AudioService extends IAudioService.Stub {
private void readPersistedSettings() {
final ContentResolver cr = mContentResolver;
- mRingerMode = System.getInt(cr, System.MODE_RINGER, AudioManager.RINGER_MODE_NORMAL);
+ int ringerMode = System.getInt(cr, System.MODE_RINGER, AudioManager.RINGER_MODE_NORMAL);
// sanity check in case the settings are restored from a device with incompatible
// ringer modes
- if (!AudioManager.isValidRingerMode(mRingerMode)) {
- mRingerMode = AudioManager.RINGER_MODE_NORMAL;
- System.putInt(cr, System.MODE_RINGER, mRingerMode);
+ if (!AudioManager.isValidRingerMode(ringerMode)) {
+ ringerMode = AudioManager.RINGER_MODE_NORMAL;
+ System.putInt(cr, System.MODE_RINGER, ringerMode);
+ }
+ synchronized(mSettingsLock) {
+ mRingerMode = ringerMode;
}
mVibrateSetting = System.getInt(cr, System.VIBRATE_ON, 0);
@@ -469,7 +485,7 @@ public class AudioService extends IAudioService.Stub {
// Each stream will read its own persisted settings
// Broadcast the sticky intent
- broadcastRingerMode();
+ broadcastRingerMode(ringerMode);
// Broadcast vibrate settings
broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER);
@@ -479,10 +495,6 @@ public class AudioService extends IAudioService.Stub {
restoreMediaButtonReceiver();
}
- private void setStreamVolumeIndex(int stream, int index) {
- AudioSystem.setStreamVolumeIndex(stream, (index + 5)/10);
- }
-
private int rescaleIndex(int index, int srcStream, int dstStream) {
return (index * mStreamStates[dstStream].getMaxIndex() + mStreamStates[srcStream].getMaxIndex() / 2) / mStreamStates[srcStream].getMaxIndex();
}
@@ -526,7 +538,11 @@ public class AudioService extends IAudioService.Stub {
// checkForRingerModeChange() in place of STREAM_RING or STREAM_NOTIFICATION)
int streamTypeAlias = STREAM_VOLUME_ALIAS[streamType];
VolumeStreamState streamState = mStreamStates[streamTypeAlias];
- final int oldIndex = (streamState.muteCount() != 0) ? streamState.mLastAudibleIndex : streamState.mIndex;
+
+ final int device = getDeviceForStream(streamTypeAlias);
+ // get last audible index if stream is muted, current index otherwise
+ final int oldIndex = streamState.getIndex(device,
+ (streamState.muteCount() != 0) /* lastAudible */);
boolean adjustVolume = true;
// If either the client forces allowing ringer modes for this adjustment,
@@ -534,8 +550,9 @@ public class AudioService extends IAudioService.Stub {
if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
streamTypeAlias == AudioSystem.STREAM_RING ||
(!mVoiceCapable && streamTypeAlias == AudioSystem.STREAM_MUSIC)) {
+ int ringerMode = getRingerMode();
// do not vibrate if already in vibrate mode
- if (mRingerMode == AudioManager.RINGER_MODE_VIBRATE) {
+ if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) {
flags &= ~AudioManager.FLAG_VIBRATE;
}
// Check if the ringer mode changes with this volume adjustment. If
@@ -554,22 +571,32 @@ public class AudioService extends IAudioService.Stub {
if (STREAM_VOLUME_ALIAS[i] == streamTypeAlias) {
VolumeStreamState s = mStreamStates[i];
- s.adjustLastAudibleIndex(direction);
+ s.adjustLastAudibleIndex(direction, device);
// Post a persist volume msg
- sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, i,
- SENDMSG_REPLACE, 0, 1, s, PERSIST_DELAY);
+ sendMsg(mAudioHandler,
+ MSG_PERSIST_VOLUME,
+ SENDMSG_REPLACE,
+ PERSIST_LAST_AUDIBLE,
+ device,
+ s,
+ PERSIST_DELAY);
}
}
}
- index = streamState.mLastAudibleIndex;
+ index = streamState.getIndex(device, true /* lastAudible */);
} else {
- if (adjustVolume && streamState.adjustIndex(direction)) {
+ if (adjustVolume && streamState.adjustIndex(direction, device)) {
// Post message to set system volume (it in turn will post a message
// to persist). Do not change volume if stream is muted.
- sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, streamTypeAlias, SENDMSG_NOOP, 0, 0,
- streamState, 0);
+ sendMsg(mAudioHandler,
+ MSG_SET_DEVICE_VOLUME,
+ SENDMSG_NOOP,
+ device,
+ 0,
+ streamState,
+ 0);
}
- index = streamState.mIndex;
+ index = streamState.getIndex(device, false /* lastAudible */);
}
sendVolumeUpdate(streamType, oldIndex, index, flags);
@@ -580,29 +607,35 @@ public class AudioService extends IAudioService.Stub {
ensureValidStreamType(streamType);
VolumeStreamState streamState = mStreamStates[STREAM_VOLUME_ALIAS[streamType]];
- final int oldIndex = (streamState.muteCount() != 0) ? streamState.mLastAudibleIndex : streamState.mIndex;
+ final int device = getDeviceForStream(streamType);
+ // get last audible index if stream is muted, current index otherwise
+ final int oldIndex = streamState.getIndex(device,
+ (streamState.muteCount() != 0) /* lastAudible */);
// setting ring or notifications volume to 0 on voice capable devices enters silent mode
if (mVoiceCapable && (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
(STREAM_VOLUME_ALIAS[streamType] == AudioSystem.STREAM_RING))) {
- int newRingerMode = mRingerMode;
+ int newRingerMode;
if (index == 0) {
newRingerMode = System.getInt(mContentResolver, System.VIBRATE_IN_SILENT, 1) == 1
? AudioManager.RINGER_MODE_VIBRATE
: AudioManager.RINGER_MODE_SILENT;
- setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, false, true);
+ setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType],
+ index,
+ device,
+ false,
+ true);
} else {
newRingerMode = AudioManager.RINGER_MODE_NORMAL;
}
- if (newRingerMode != mRingerMode) {
- setRingerMode(newRingerMode);
- }
+ setRingerMode(newRingerMode);
}
index = rescaleIndex(index * 10, streamType, STREAM_VOLUME_ALIAS[streamType]);
- setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, false, true);
-
- index = (streamState.muteCount() != 0) ? streamState.mLastAudibleIndex : streamState.mIndex;
+ setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, device, false, true);
+ // get last audible index if stream is muted, current index otherwise
+ index = streamState.getIndex(device,
+ (streamState.muteCount() != 0) /* lastAudible */);
sendVolumeUpdate(streamType, oldIndex, index, flags);
}
@@ -630,28 +663,43 @@ public class AudioService extends IAudioService.Stub {
*
* @param streamType Type of the stream
* @param index Desired volume index of the stream
+ * @param device the device whose volume must be changed
* @param force If true, set the volume even if the desired volume is same
* as the current volume.
* @param lastAudible If true, stores new index as last audible one
*/
- private void setStreamVolumeInt(int streamType, int index, boolean force, boolean lastAudible) {
+ private void setStreamVolumeInt(int streamType,
+ int index,
+ int device,
+ boolean force,
+ boolean lastAudible) {
VolumeStreamState streamState = mStreamStates[streamType];
// If stream is muted, set last audible index only
if (streamState.muteCount() != 0) {
// Do not allow last audible index to be 0
if (index != 0) {
- streamState.setLastAudibleIndex(index);
+ streamState.setLastAudibleIndex(index, device);
// Post a persist volume msg
- sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamType,
- SENDMSG_REPLACE, 0, 1, streamState, PERSIST_DELAY);
+ sendMsg(mAudioHandler,
+ MSG_PERSIST_VOLUME,
+ SENDMSG_REPLACE,
+ PERSIST_LAST_AUDIBLE,
+ device,
+ streamState,
+ PERSIST_DELAY);
}
} else {
- if (streamState.setIndex(index, lastAudible) || force) {
+ if (streamState.setIndex(index, device, lastAudible) || force) {
// Post message to set system volume (it in turn will post a message
// to persist).
- sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, streamType, SENDMSG_NOOP, 0, 0,
- streamState, 0);
+ sendMsg(mAudioHandler,
+ MSG_SET_DEVICE_VOLUME,
+ SENDMSG_NOOP,
+ device,
+ 0,
+ streamState,
+ 0);
}
}
}
@@ -680,7 +728,8 @@ public class AudioService extends IAudioService.Stub {
/** @see AudioManager#getStreamVolume(int) */
public int getStreamVolume(int streamType) {
ensureValidStreamType(streamType);
- return (mStreamStates[streamType].mIndex + 5) / 10;
+ int device = getDeviceForStream(streamType);
+ return (mStreamStates[streamType].getIndex(device, false /* lastAudible */) + 5) / 10;
}
/** @see AudioManager#getStreamMaxVolume(int) */
@@ -693,27 +742,37 @@ public class AudioService extends IAudioService.Stub {
/** Get last audible volume before stream was muted. */
public int getLastAudibleStreamVolume(int streamType) {
ensureValidStreamType(streamType);
- return (mStreamStates[streamType].mLastAudibleIndex + 5) / 10;
+ int device = getDeviceForStream(streamType);
+ return (mStreamStates[streamType].getIndex(device, true /* lastAudible */) + 5) / 10;
}
/** @see AudioManager#getRingerMode() */
public int getRingerMode() {
- return mRingerMode;
+ synchronized(mSettingsLock) {
+ return mRingerMode;
+ }
+ }
+
+ private void ensureValidRingerMode(int ringerMode) {
+ if (!AudioManager.isValidRingerMode(ringerMode)) {
+ throw new IllegalArgumentException("Bad ringer mode " + ringerMode);
+ }
}
/** @see AudioManager#setRingerMode(int) */
public void setRingerMode(int ringerMode) {
- synchronized (mSettingsLock) {
- if (ringerMode != mRingerMode) {
- setRingerModeInt(ringerMode, true);
- // Send sticky broadcast
- broadcastRingerMode();
- }
+ ensureValidRingerMode(ringerMode);
+ if (ringerMode != getRingerMode()) {
+ setRingerModeInt(ringerMode, true);
+ // Send sticky broadcast
+ broadcastRingerMode(ringerMode);
}
}
private void setRingerModeInt(int ringerMode, boolean persist) {
- mRingerMode = ringerMode;
+ synchronized(mSettingsLock) {
+ mRingerMode = ringerMode;
+ }
// Mute stream if not previously muted by ringer mode and ringer mode
// is not RINGER_MODE_NORMAL and stream is affected by ringer mode.
@@ -723,20 +782,27 @@ public class AudioService extends IAudioService.Stub {
for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
if (isStreamMutedByRingerMode(streamType)) {
if (!isStreamAffectedByRingerMode(streamType) ||
- mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
+ ringerMode == AudioManager.RINGER_MODE_NORMAL) {
// ring and notifications volume should never be 0 when not silenced
// on voice capable devices
if (mVoiceCapable &&
- STREAM_VOLUME_ALIAS[streamType] == AudioSystem.STREAM_RING &&
- mStreamStates[streamType].mLastAudibleIndex == 0) {
- mStreamStates[streamType].mLastAudibleIndex = 10;
+ STREAM_VOLUME_ALIAS[streamType] == AudioSystem.STREAM_RING) {
+
+ Set set = mStreamStates[streamType].mLastAudibleIndex.entrySet();
+ Iterator i = set.iterator();
+ while (i.hasNext()) {
+ Map.Entry entry = (Map.Entry)i.next();
+ if ((Integer)entry.getValue() == 0) {
+ entry.setValue(10);
+ }
+ }
}
mStreamStates[streamType].mute(null, false);
mRingerModeMutedStreams &= ~(1 << streamType);
}
} else {
if (isStreamAffectedByRingerMode(streamType) &&
- mRingerMode != AudioManager.RINGER_MODE_NORMAL) {
+ ringerMode != AudioManager.RINGER_MODE_NORMAL) {
mStreamStates[streamType].mute(null, true);
mRingerModeMutedStreams |= (1 << streamType);
}
@@ -745,7 +811,7 @@ public class AudioService extends IAudioService.Stub {
// Post a persist ringer mode msg
if (persist) {
- sendMsg(mAudioHandler, MSG_PERSIST_RINGER_MODE, SHARED_MSG,
+ sendMsg(mAudioHandler, MSG_PERSIST_RINGER_MODE,
SENDMSG_REPLACE, 0, 0, null, PERSIST_DELAY);
}
}
@@ -756,10 +822,10 @@ public class AudioService extends IAudioService.Stub {
switch (getVibrateSetting(vibrateType)) {
case AudioManager.VIBRATE_SETTING_ON:
- return mRingerMode != AudioManager.RINGER_MODE_SILENT;
+ return getRingerMode() != AudioManager.RINGER_MODE_SILENT;
case AudioManager.VIBRATE_SETTING_ONLY_SILENT:
- return mRingerMode == AudioManager.RINGER_MODE_VIBRATE;
+ return getRingerMode() == AudioManager.RINGER_MODE_VIBRATE;
case AudioManager.VIBRATE_SETTING_OFF:
// return false, even for incoming calls
@@ -785,7 +851,7 @@ public class AudioService extends IAudioService.Stub {
// Post message to set ringer mode (it in turn will post a message
// to persist)
- sendMsg(mAudioHandler, MSG_PERSIST_VIBRATE_SETTING, SHARED_MSG, SENDMSG_NOOP, 0, 0,
+ sendMsg(mAudioHandler, MSG_PERSIST_VIBRATE_SETTING, SENDMSG_NOOP, 0, 0,
null, 0);
}
@@ -926,8 +992,6 @@ public class AudioService extends IAudioService.Stub {
if (mode != mMode) {
status = AudioSystem.setPhoneState(mode);
if (status == AudioSystem.AUDIO_STATUS_OK) {
- // automatically handle audio focus for mode changes
- handleFocusForCalls(mMode, mode, cb);
mMode = mode;
} else {
if (hdlr != null) {
@@ -951,46 +1015,13 @@ public class AudioService extends IAudioService.Stub {
}
}
int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
- int index = mStreamStates[STREAM_VOLUME_ALIAS[streamType]].mIndex;
- setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, true, false);
+ int device = getDeviceForStream(streamType);
+ int index = mStreamStates[STREAM_VOLUME_ALIAS[streamType]].getIndex(device, false);
+ setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, device, true, false);
}
return newModeOwnerPid;
}
- /** pre-condition: oldMode != newMode */
- private void handleFocusForCalls(int oldMode, int newMode, IBinder cb) {
- // if ringing
- if (newMode == AudioSystem.MODE_RINGTONE) {
- // if not ringing silently
- int ringVolume = AudioService.this.getStreamVolume(AudioManager.STREAM_RING);
- if (ringVolume > 0) {
- // request audio focus for the communication focus entry
- requestAudioFocus(AudioManager.STREAM_RING,
- AudioManager.AUDIOFOCUS_GAIN_TRANSIENT, cb,
- null /* IAudioFocusDispatcher allowed to be null only for this clientId */,
- IN_VOICE_COMM_FOCUS_ID /*clientId*/,
- "system");
-
- }
- }
- // if entering call
- else if ((newMode == AudioSystem.MODE_IN_CALL)
- || (newMode == AudioSystem.MODE_IN_COMMUNICATION)) {
- // request audio focus for the communication focus entry
- // (it's ok if focus was already requested during ringing)
- requestAudioFocus(AudioManager.STREAM_RING,
- AudioManager.AUDIOFOCUS_GAIN_TRANSIENT, cb,
- null /* IAudioFocusDispatcher allowed to be null only for this clientId */,
- IN_VOICE_COMM_FOCUS_ID /*clientId*/,
- "system");
- }
- // if exiting call
- else if (newMode == AudioSystem.MODE_NORMAL) {
- // abandon audio focus for communication focus entry
- abandonAudioFocus(null, IN_VOICE_COMM_FOCUS_ID);
- }
- }
-
/** @see AudioManager#getMode() */
public int getMode() {
return mMode;
@@ -998,20 +1029,20 @@ public class AudioService extends IAudioService.Stub {
/** @see AudioManager#playSoundEffect(int) */
public void playSoundEffect(int effectType) {
- sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SHARED_MSG, SENDMSG_NOOP,
+ sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SENDMSG_NOOP,
effectType, -1, null, 0);
}
/** @see AudioManager#playSoundEffect(int, float) */
public void playSoundEffectVolume(int effectType, float volume) {
loadSoundEffects();
- sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SHARED_MSG, SENDMSG_NOOP,
+ sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SENDMSG_NOOP,
effectType, (int) (volume * 1000), null, 0);
}
/**
* Loads samples into the soundpool.
- * This method must be called at when sound effects are enabled
+ * This method must be called at first when sound effects are enabled
*/
public boolean loadSoundEffects() {
int status;
@@ -1026,10 +1057,6 @@ public class AudioService extends IAudioService.Stub {
return true;
}
mSoundPool = new SoundPool(NUM_SOUNDPOOL_CHANNELS, AudioSystem.STREAM_SYSTEM, 0);
- if (mSoundPool == null) {
- Log.w(TAG, "loadSoundEffects() could not allocate sound pool");
- return false;
- }
try {
mSoundPoolCallBack = null;
@@ -1220,28 +1247,7 @@ public class AudioService extends IAudioService.Stub {
for (int streamType = 0; streamType < numStreamTypes; streamType++) {
VolumeStreamState streamState = mStreamStates[streamType];
- String settingName = System.VOLUME_SETTINGS[STREAM_VOLUME_ALIAS[streamType]];
- String lastAudibleSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE;
- int index = Settings.System.getInt(mContentResolver,
- settingName,
- AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
- if (STREAM_VOLUME_ALIAS[streamType] != streamType) {
- index = rescaleIndex(index * 10, STREAM_VOLUME_ALIAS[streamType], streamType);
- } else {
- index *= 10;
- }
- streamState.mIndex = streamState.getValidIndex(index);
-
- index = (index + 5) / 10;
- index = Settings.System.getInt(mContentResolver,
- lastAudibleSettingName,
- (index > 0) ? index : AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
- if (STREAM_VOLUME_ALIAS[streamType] != streamType) {
- index = rescaleIndex(index * 10, STREAM_VOLUME_ALIAS[streamType], streamType);
- } else {
- index *= 10;
- }
- streamState.mLastAudibleIndex = streamState.getValidIndex(index);
+ streamState.readSettings();
// unmute stream that was muted but is not affect by mute anymore
if (streamState.muteCount() != 0 && !isStreamAffectedByMute(streamType)) {
@@ -1253,7 +1259,7 @@ public class AudioService extends IAudioService.Stub {
}
// apply stream volume
if (streamState.muteCount() == 0) {
- setStreamVolumeIndex(streamType, streamState.mIndex);
+ streamState.applyAllVolumes();
}
}
@@ -1268,7 +1274,7 @@ public class AudioService extends IAudioService.Stub {
}
mForcedUseForComm = on ? AudioSystem.FORCE_SPEAKER : AudioSystem.FORCE_NONE;
- sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SHARED_MSG, SENDMSG_QUEUE,
+ sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE,
AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, null, 0);
}
@@ -1284,9 +1290,9 @@ public class AudioService extends IAudioService.Stub {
}
mForcedUseForComm = on ? AudioSystem.FORCE_BT_SCO : AudioSystem.FORCE_NONE;
- sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SHARED_MSG, SENDMSG_QUEUE,
+ sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE,
AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, null, 0);
- sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SHARED_MSG, SENDMSG_QUEUE,
+ sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE,
AudioSystem.FOR_RECORD, mForcedUseForComm, null, 0);
}
@@ -1526,7 +1532,7 @@ public class AudioService extends IAudioService.Stub {
// without delay to reset the SCO audio state and clear SCO clients.
// If we could get a proxy, send a delayed failure message that will reset our state
// in case we don't receive onServiceConnected().
- sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED, 0,
+ sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED,
SENDMSG_REPLACE, 0, 0, null, result ? BT_HEADSET_CNCT_TIMEOUT_MS : 0);
return result;
}
@@ -1540,7 +1546,7 @@ public class AudioService extends IAudioService.Stub {
if (mBluetoothHeadset != null) {
if (!mBluetoothHeadset.stopVoiceRecognition(
mBluetoothHeadsetDevice)) {
- sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED, 0,
+ sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED,
SENDMSG_REPLACE, 0, 0, null, 0);
}
} else if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL &&
@@ -1623,7 +1629,7 @@ public class AudioService extends IAudioService.Stub {
}
}
if (!status) {
- sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED, 0,
+ sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED,
SENDMSG_REPLACE, 0, 0, null, 0);
}
}
@@ -1668,11 +1674,12 @@ public class AudioService extends IAudioService.Stub {
*/
private boolean checkForRingerModeChange(int oldIndex, int direction, int streamType) {
boolean adjustVolumeIndex = true;
- int newRingerMode = mRingerMode;
+ int ringerMode = getRingerMode();
+ int newRingerMode = ringerMode;
int uiIndex = (oldIndex + 5) / 10;
boolean vibeInSilent = System.getInt(mContentResolver, System.VIBRATE_IN_SILENT, 1) == 1;
- if (mRingerMode == RINGER_MODE_NORMAL) {
+ if (ringerMode == RINGER_MODE_NORMAL) {
if ((direction == AudioManager.ADJUST_LOWER) && (uiIndex <= 1)) {
// enter silent mode if current index is the last audible one and not repeating a
// volume key down
@@ -1687,7 +1694,7 @@ public class AudioService extends IAudioService.Stub {
adjustVolumeIndex = false;
}
}
- } else if (mRingerMode == RINGER_MODE_VIBRATE) {
+ } else if (ringerMode == RINGER_MODE_VIBRATE) {
if ((direction == AudioManager.ADJUST_LOWER)) {
// Set it to silent, if it wasn't a long-press
if (mPrevVolDirection != AudioManager.ADJUST_LOWER) {
@@ -1706,9 +1713,7 @@ public class AudioService extends IAudioService.Stub {
adjustVolumeIndex = false;
}
- if (newRingerMode != mRingerMode) {
- setRingerMode(newRingerMode);
- }
+ setRingerMode(newRingerMode);
mPrevVolDirection = direction;
@@ -1798,10 +1803,10 @@ public class AudioService extends IAudioService.Stub {
}
}
- private void broadcastRingerMode() {
+ private void broadcastRingerMode(int ringerMode) {
// Send sticky broadcast
Intent broadcast = new Intent(AudioManager.RINGER_MODE_CHANGED_ACTION);
- broadcast.putExtra(AudioManager.EXTRA_RINGER_MODE, mRingerMode);
+ 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();
@@ -1820,17 +1825,9 @@ public class AudioService extends IAudioService.Stub {
}
// Message helper methods
- private static int getMsg(int baseMsg, int streamType) {
- return (baseMsg & 0xffff) | streamType << 16;
- }
- private static int getMsgBase(int msg) {
- return msg & 0xffff;
- }
-
- private static void sendMsg(Handler handler, int baseMsg, int streamType,
+ private static void sendMsg(Handler handler, int msg,
int existingMsgPolicy, int arg1, int arg2, Object obj, int delay) {
- int msg = (streamType == SHARED_MSG) ? baseMsg : getMsg(baseMsg, streamType);
if (existingMsgPolicy == SENDMSG_REPLACE) {
handler.removeMessages(msg);
@@ -1838,8 +1835,7 @@ public class AudioService extends IAudioService.Stub {
return;
}
- handler
- .sendMessageDelayed(handler.obtainMessage(msg, arg1, arg2, obj), delay);
+ handler.sendMessageDelayed(handler.obtainMessage(msg, arg1, arg2, obj), delay);
}
boolean checkAudioSettingsPermission(String method) {
@@ -1854,6 +1850,22 @@ public class AudioService extends IAudioService.Stub {
return false;
}
+ private int getDeviceForStream(int stream) {
+ int device = AudioSystem.getDevicesForStream(stream);
+ if ((device & (device - 1)) != 0) {
+ // Multiple device selection is either:
+ // - speaker + one other device: give priority to speaker in this case.
+ // - one A2DP device + another device: happens with duplicated output. In this case
+ // retain the device on the A2DP output as the other must not correspond to an active
+ // selection if not the speaker.
+ if ((device & AudioSystem.DEVICE_OUT_SPEAKER) != 0) {
+ device = AudioSystem.DEVICE_OUT_SPEAKER;
+ } else {
+ device &= AudioSystem.DEVICE_OUT_ALL_A2DP;
+ }
+ }
+ return device;
+ }
///////////////////////////////////////////////////////////////////////////
// Inner classes
@@ -1865,54 +1877,127 @@ public class AudioService extends IAudioService.Stub {
private String mVolumeIndexSettingName;
private String mLastAudibleVolumeIndexSettingName;
private int mIndexMax;
- private int mIndex;
- private int mLastAudibleIndex;
- private ArrayList<VolumeDeathHandler> mDeathHandlers; //handles mute/solo requests client death
+ private final HashMap <Integer, Integer> mIndex = new HashMap <Integer, Integer>();
+ private final HashMap <Integer, Integer> mLastAudibleIndex =
+ new HashMap <Integer, Integer>();
+ private ArrayList<VolumeDeathHandler> mDeathHandlers; //handles mute/solo clients death
private VolumeStreamState(String settingName, int streamType) {
- setVolumeIndexSettingName(settingName);
+ mVolumeIndexSettingName = settingName;
+ mLastAudibleVolumeIndexSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE;
mStreamType = streamType;
-
- final ContentResolver cr = mContentResolver;
mIndexMax = MAX_STREAM_VOLUME[streamType];
- mIndex = Settings.System.getInt(cr,
- mVolumeIndexSettingName,
- AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
- mLastAudibleIndex = Settings.System.getInt(cr,
- mLastAudibleVolumeIndexSettingName,
- (mIndex > 0) ? mIndex : AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
AudioSystem.initStreamVolume(streamType, 0, mIndexMax);
mIndexMax *= 10;
- mIndex = getValidIndex(10 * mIndex);
- mLastAudibleIndex = getValidIndex(10 * mLastAudibleIndex);
- setStreamVolumeIndex(streamType, mIndex);
+
+ readSettings();
+
+ applyAllVolumes();
+
mDeathHandlers = new ArrayList<VolumeDeathHandler>();
}
- public void setVolumeIndexSettingName(String settingName) {
- mVolumeIndexSettingName = settingName;
- mLastAudibleVolumeIndexSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE;
+ public String getSettingNameForDevice(boolean lastAudible, int device) {
+ String name = lastAudible ?
+ mLastAudibleVolumeIndexSettingName :
+ mVolumeIndexSettingName;
+ String suffix = AudioSystem.getDeviceName(device);
+ if (suffix.isEmpty()) {
+ return name;
+ }
+ return name + "_" + suffix;
+ }
+
+ public void readSettings() {
+ int index = Settings.System.getInt(mContentResolver,
+ mVolumeIndexSettingName,
+ AudioManager.DEFAULT_STREAM_VOLUME[mStreamType]);
+
+ mIndex.clear();
+ mIndex.put(AudioSystem.DEVICE_OUT_DEFAULT, index);
+
+ index = Settings.System.getInt(mContentResolver,
+ mLastAudibleVolumeIndexSettingName,
+ (index > 0) ? index : AudioManager.DEFAULT_STREAM_VOLUME[mStreamType]);
+ mLastAudibleIndex.clear();
+ mLastAudibleIndex.put(AudioSystem.DEVICE_OUT_DEFAULT, index);
+
+ int remainingDevices = AudioSystem.DEVICE_OUT_ALL;
+ for (int i = 0; remainingDevices != 0; i++) {
+ int device = (1 << i);
+ if ((device & remainingDevices) == 0) {
+ continue;
+ }
+ remainingDevices &= ~device;
+
+ // retrieve current volume for device
+ String name = getSettingNameForDevice(false, device);
+ index = Settings.System.getInt(mContentResolver, name, -1);
+ if (index == -1) {
+ continue;
+ }
+ mIndex.put(device, getValidIndex(10 * index));
+
+ // retrieve last audible volume for device
+ name = getSettingNameForDevice(true, device);
+ index = Settings.System.getInt(mContentResolver, name, -1);
+ mLastAudibleIndex.put(device, getValidIndex(10 * index));
+ }
}
- public boolean adjustIndex(int deltaIndex) {
- return setIndex(mIndex + deltaIndex * 10, true);
+ public void applyDeviceVolume(int device) {
+ AudioSystem.setStreamVolumeIndex(mStreamType,
+ (getIndex(device, false /* lastAudible */) + 5)/10,
+ device);
}
- public boolean setIndex(int index, boolean lastAudible) {
- int oldIndex = mIndex;
- mIndex = getValidIndex(index);
+ public void applyAllVolumes() {
+ // apply default volume first: by convention this will reset all
+ // devices volumes in audio policy manager to the supplied value
+ AudioSystem.setStreamVolumeIndex(mStreamType,
+ (getIndex(AudioSystem.DEVICE_OUT_DEFAULT, false /* lastAudible */) + 5)/10,
+ AudioSystem.DEVICE_OUT_DEFAULT);
+ // then apply device specific volumes
+ Set set = mIndex.entrySet();
+ Iterator i = set.iterator();
+ while (i.hasNext()) {
+ Map.Entry entry = (Map.Entry)i.next();
+ int device = ((Integer)entry.getKey()).intValue();
+ if (device != AudioSystem.DEVICE_OUT_DEFAULT) {
+ AudioSystem.setStreamVolumeIndex(mStreamType,
+ ((Integer)entry.getValue() + 5)/10,
+ device);
+ }
+ }
+ }
+
+ public boolean adjustIndex(int deltaIndex, int device) {
+ return setIndex(getIndex(device,
+ false /* lastAudible */) + deltaIndex * 10,
+ device,
+ true /* lastAudible */);
+ }
- if (oldIndex != mIndex) {
+ public boolean setIndex(int index, int device, boolean lastAudible) {
+ int oldIndex = getIndex(device, false /* lastAudible */);
+ index = getValidIndex(index);
+ mIndex.put(device, getValidIndex(index));
+
+ if (oldIndex != index) {
if (lastAudible) {
- mLastAudibleIndex = mIndex;
+ mLastAudibleIndex.put(device, index);
}
// Apply change to all streams using this one as alias
int numStreamTypes = AudioSystem.getNumStreamTypes();
for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
if (streamType != mStreamType && STREAM_VOLUME_ALIAS[streamType] == mStreamType) {
- mStreamStates[streamType].setIndex(rescaleIndex(mIndex, mStreamType, streamType), lastAudible);
+ mStreamStates[streamType].setIndex(rescaleIndex(index,
+ mStreamType,
+ streamType),
+ device,
+ lastAudible);
}
}
return true;
@@ -1921,12 +2006,29 @@ public class AudioService extends IAudioService.Stub {
}
}
- public void setLastAudibleIndex(int index) {
- mLastAudibleIndex = getValidIndex(index);
+ public int getIndex(int device, boolean lastAudible) {
+ HashMap <Integer, Integer> indexes;
+ if (lastAudible) {
+ indexes = mLastAudibleIndex;
+ } else {
+ indexes = mIndex;
+ }
+ Integer index = indexes.get(device);
+ if (index == null) {
+ // there is always an entry for AudioSystem.DEVICE_OUT_DEFAULT
+ index = indexes.get(AudioSystem.DEVICE_OUT_DEFAULT);
+ }
+ return index.intValue();
+ }
+
+ public void setLastAudibleIndex(int index, int device) {
+ mLastAudibleIndex.put(device, getValidIndex(index));
}
- public void adjustLastAudibleIndex(int deltaIndex) {
- setLastAudibleIndex(mLastAudibleIndex + deltaIndex * 10);
+ public void adjustLastAudibleIndex(int deltaIndex, int device) {
+ setLastAudibleIndex(getIndex(device,
+ true /* lastAudible */) + deltaIndex * 10,
+ device);
}
public int getMaxIndex() {
@@ -1971,10 +2073,20 @@ public class AudioService extends IAudioService.Stub {
mICallback.linkToDeath(this, 0);
}
mDeathHandlers.add(this);
- // If the stream is not yet muted by any client, set lvel to 0
+ // If the stream is not yet muted by any client, set level to 0
if (muteCount() == 0) {
- setIndex(0, false);
- sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, mStreamType, SENDMSG_NOOP, 0, 0,
+ Set set = mIndex.entrySet();
+ Iterator i = set.iterator();
+ while (i.hasNext()) {
+ Map.Entry entry = (Map.Entry)i.next();
+ int device = ((Integer)entry.getKey()).intValue();
+ setIndex(0, device, false /* lastAudible */);
+ }
+ sendMsg(mAudioHandler,
+ MSG_SET_ALL_VOLUMES,
+ SENDMSG_NOOP,
+ 0,
+ 0,
VolumeStreamState.this, 0);
}
} catch (RemoteException e) {
@@ -2002,9 +2114,23 @@ public class AudioService extends IAudioService.Stub {
if (muteCount() == 0) {
// If the stream is not muted any more, restore it's volume if
// ringer mode allows it
- if (!isStreamAffectedByRingerMode(mStreamType) || mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
- setIndex(mLastAudibleIndex, false);
- sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, mStreamType, SENDMSG_NOOP, 0, 0,
+ if (!isStreamAffectedByRingerMode(mStreamType) ||
+ mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
+ Set set = mIndex.entrySet();
+ Iterator i = set.iterator();
+ while (i.hasNext()) {
+ Map.Entry entry = (Map.Entry)i.next();
+ int device = ((Integer)entry.getKey()).intValue();
+ setIndex(getIndex(device,
+ true /* lastAudible */),
+ device,
+ false /* lastAudible */);
+ }
+ sendMsg(mAudioHandler,
+ MSG_SET_ALL_VOLUMES,
+ SENDMSG_NOOP,
+ 0,
+ 0,
VolumeStreamState.this, 0);
}
}
@@ -2083,38 +2209,63 @@ public class AudioService extends IAudioService.Stub {
/** Handles internal volume messages in separate volume thread. */
private class AudioHandler extends Handler {
- private void setSystemVolume(VolumeStreamState streamState) {
+ private void setDeviceVolume(VolumeStreamState streamState, int device) {
- // Adjust volume
- setStreamVolumeIndex(streamState.mStreamType, streamState.mIndex);
+ // Apply volume
+ streamState.applyDeviceVolume(device);
// Apply change to all streams using this one as alias
int numStreamTypes = AudioSystem.getNumStreamTypes();
for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
if (streamType != streamState.mStreamType &&
- STREAM_VOLUME_ALIAS[streamType] == streamState.mStreamType) {
- setStreamVolumeIndex(streamType, mStreamStates[streamType].mIndex);
+ STREAM_VOLUME_ALIAS[streamType] == streamState.mStreamType) {
+ mStreamStates[streamType].applyDeviceVolume(device);
}
}
// Post a persist volume msg
- sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamState.mStreamType,
- SENDMSG_REPLACE, 1, 1, streamState, PERSIST_DELAY);
+ sendMsg(mAudioHandler,
+ MSG_PERSIST_VOLUME,
+ SENDMSG_REPLACE,
+ PERSIST_CURRENT|PERSIST_LAST_AUDIBLE,
+ device,
+ streamState,
+ PERSIST_DELAY);
+
}
- private void persistVolume(VolumeStreamState streamState, boolean current, boolean lastAudible) {
- if (current) {
- System.putInt(mContentResolver, streamState.mVolumeIndexSettingName,
- (streamState.mIndex + 5)/ 10);
+ private void setAllVolumes(VolumeStreamState streamState) {
+
+ // Apply volume
+ streamState.applyAllVolumes();
+
+ // Apply change to all streams using this one as alias
+ int numStreamTypes = AudioSystem.getNumStreamTypes();
+ for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
+ if (streamType != streamState.mStreamType &&
+ STREAM_VOLUME_ALIAS[streamType] == streamState.mStreamType) {
+ mStreamStates[streamType].applyAllVolumes();
+ }
}
- if (lastAudible) {
- System.putInt(mContentResolver, streamState.mLastAudibleVolumeIndexSettingName,
- (streamState.mLastAudibleIndex + 5) / 10);
+ }
+
+ private void persistVolume(VolumeStreamState streamState,
+ int persistType,
+ int device) {
+ if ((persistType & PERSIST_CURRENT) != 0) {
+ System.putInt(mContentResolver,
+ streamState.getSettingNameForDevice(false /* lastAudible */, device),
+ (streamState.getIndex(device, false /* lastAudible */) + 5)/ 10);
+ }
+ if ((persistType & PERSIST_LAST_AUDIBLE) != 0) {
+ System.putInt(mContentResolver,
+ streamState.getSettingNameForDevice(true /* lastAudible */, device),
+ (streamState.getIndex(device, true /* lastAudible */) + 5) / 10);
}
}
- private void persistRingerMode() {
- System.putInt(mContentResolver, System.MODE_RINGER, mRingerMode);
+ private void persistRingerMode(int ringerMode) {
+ System.putInt(mContentResolver, System.MODE_RINGER, ringerMode);
}
private void persistVibrateSetting() {
@@ -2138,32 +2289,30 @@ public class AudioService extends IAudioService.Stub {
mSoundPool.play(SOUND_EFFECT_FILES_MAP[effectType][1], volFloat, volFloat, 0, 0, 1.0f);
} else {
MediaPlayer mediaPlayer = new MediaPlayer();
- if (mediaPlayer != null) {
- try {
- String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effectType][0]];
- mediaPlayer.setDataSource(filePath);
- mediaPlayer.setAudioStreamType(AudioSystem.STREAM_SYSTEM);
- mediaPlayer.prepare();
- mediaPlayer.setVolume(volFloat, volFloat);
- mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
- public void onCompletion(MediaPlayer mp) {
- cleanupPlayer(mp);
- }
- });
- mediaPlayer.setOnErrorListener(new OnErrorListener() {
- public boolean onError(MediaPlayer mp, int what, int extra) {
- cleanupPlayer(mp);
- return true;
- }
- });
- mediaPlayer.start();
- } catch (IOException ex) {
- Log.w(TAG, "MediaPlayer IOException: "+ex);
- } catch (IllegalArgumentException ex) {
- Log.w(TAG, "MediaPlayer IllegalArgumentException: "+ex);
- } catch (IllegalStateException ex) {
- Log.w(TAG, "MediaPlayer IllegalStateException: "+ex);
- }
+ try {
+ String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effectType][0]];
+ mediaPlayer.setDataSource(filePath);
+ mediaPlayer.setAudioStreamType(AudioSystem.STREAM_SYSTEM);
+ mediaPlayer.prepare();
+ mediaPlayer.setVolume(volFloat, volFloat);
+ mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
+ public void onCompletion(MediaPlayer mp) {
+ cleanupPlayer(mp);
+ }
+ });
+ mediaPlayer.setOnErrorListener(new OnErrorListener() {
+ public boolean onError(MediaPlayer mp, int what, int extra) {
+ cleanupPlayer(mp);
+ return true;
+ }
+ });
+ mediaPlayer.start();
+ } catch (IOException ex) {
+ Log.w(TAG, "MediaPlayer IOException: "+ex);
+ } catch (IllegalArgumentException ex) {
+ Log.w(TAG, "MediaPlayer IllegalArgumentException: "+ex);
+ } catch (IllegalStateException ex) {
+ Log.w(TAG, "MediaPlayer IllegalStateException: "+ex);
}
}
}
@@ -2191,20 +2340,25 @@ public class AudioService extends IAudioService.Stub {
@Override
public void handleMessage(Message msg) {
- int baseMsgWhat = getMsgBase(msg.what);
- switch (baseMsgWhat) {
+ switch (msg.what) {
+
+ case MSG_SET_DEVICE_VOLUME:
+ setDeviceVolume((VolumeStreamState) msg.obj, msg.arg1);
+ break;
- case MSG_SET_SYSTEM_VOLUME:
- setSystemVolume((VolumeStreamState) msg.obj);
+ case MSG_SET_ALL_VOLUMES:
+ setAllVolumes((VolumeStreamState) msg.obj);
break;
case MSG_PERSIST_VOLUME:
- persistVolume((VolumeStreamState) msg.obj, (msg.arg1 != 0), (msg.arg2 != 0));
+ persistVolume((VolumeStreamState) msg.obj, msg.arg1, msg.arg2);
break;
case MSG_PERSIST_RINGER_MODE:
- persistRingerMode();
+ // note that the value persisted is the current ringer mode, not the
+ // value of ringer mode as of the time the request was made to persist
+ persistRingerMode(getRingerMode());
break;
case MSG_PERSIST_VIBRATE_SETTING:
@@ -2217,7 +2371,7 @@ public class AudioService extends IAudioService.Stub {
// Force creation of new IAudioFlinger interface so that we are notified
// when new media_server process is back to life.
AudioSystem.setErrorCallback(mAudioSystemCallback);
- sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SHARED_MSG, SENDMSG_NOOP, 0, 0,
+ sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SENDMSG_NOOP, 0, 0,
null, 500);
}
break;
@@ -2234,7 +2388,7 @@ public class AudioService extends IAudioService.Stub {
synchronized (mConnectedDevices) {
Set set = mConnectedDevices.entrySet();
Iterator i = set.iterator();
- while(i.hasNext()){
+ while (i.hasNext()) {
Map.Entry device = (Map.Entry)i.next();
AudioSystem.setDeviceConnectionState(
((Integer)device.getKey()).intValue(),
@@ -2252,15 +2406,10 @@ public class AudioService extends IAudioService.Stub {
// Restore stream volumes
int numStreamTypes = AudioSystem.getNumStreamTypes();
for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
- int index;
VolumeStreamState streamState = mStreamStates[streamType];
AudioSystem.initStreamVolume(streamType, 0, (streamState.mIndexMax + 5) / 10);
- if (streamState.muteCount() == 0) {
- index = streamState.mIndex;
- } else {
- index = 0;
- }
- setStreamVolumeIndex(streamType, index);
+
+ streamState.applyAllVolumes();
}
// Restore ringer mode
@@ -2320,6 +2469,10 @@ public class AudioService extends IAudioService.Stub {
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
+ // FIXME This synchronized is not necessary if mSettingsLock only protects mRingerMode.
+ // However there appear to be some missing locks around mRingerModeMutedStreams
+ // and mRingerModeAffectedStreams, so will leave this synchronized for now.
+ // mRingerModeMutedStreams and mMuteAffectedStreams are safe (only accessed once).
synchronized (mSettingsLock) {
int ringerModeAffectedStreams = Settings.System.getInt(mContentResolver,
Settings.System.MODE_RINGER_STREAMS_AFFECTED,
@@ -2666,7 +2819,7 @@ public class AudioService extends IAudioService.Stub {
}
} else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
mBootCompleted = true;
- sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SHARED_MSG, SENDMSG_NOOP,
+ sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SENDMSG_NOOP,
0, 0, null, 0);
mKeyguardManager =
@@ -2707,9 +2860,10 @@ public class AudioService extends IAudioService.Stub {
//==========================================================================================
/* constant to identify focus stack entry that is used to hold the focus while the phone
- * is ringing or during a call
+ * is ringing or during a call. Used by com.android.internal.telephony.CallManager when
+ * entering and exiting calls.
*/
- private final static String IN_VOICE_COMM_FOCUS_ID = "AudioFocus_For_Phone_Ring_And_Calls";
+ public final static String IN_VOICE_COMM_FOCUS_ID = "AudioFocus_For_Phone_Ring_And_Calls";
private final static Object mAudioFocusLock = new Object();
@@ -2791,7 +2945,7 @@ public class AudioService extends IAudioService.Stub {
}
}
- private Stack<FocusStackEntry> mFocusStack = new Stack<FocusStackEntry>();
+ private final Stack<FocusStackEntry> mFocusStack = new Stack<FocusStackEntry>();
/**
* Helper function:
@@ -3168,7 +3322,7 @@ public class AudioService extends IAudioService.Stub {
* synchronized on mRCStack, but also BEFORE on mFocusLock as any change in either
* stack, audio focus or RC, can lead to a change in the remote control display
*/
- private Stack<RemoteControlStackEntry> mRCStack = new Stack<RemoteControlStackEntry>();
+ private final Stack<RemoteControlStackEntry> mRCStack = new Stack<RemoteControlStackEntry>();
/**
* Helper function:
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 95d93b2..3080497 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -27,7 +27,7 @@ package android.media;
*/
public class AudioSystem
{
- /* FIXME: Need to finalize this and correlate with native layer */
+ /* These values must be kept in sync with AudioSystem.h */
/*
* If these are modified, please also update Settings.System.VOLUME_SETTINGS
* and attrs.xml and AudioManager.java.
@@ -183,6 +183,7 @@ public class AudioSystem
}
}
+
/*
* AudioPolicyService methods
*/
@@ -202,6 +203,23 @@ public class AudioSystem
public static final int DEVICE_OUT_ANLG_DOCK_HEADSET = 0x800;
public static final int DEVICE_OUT_DGTL_DOCK_HEADSET = 0x1000;
public static final int DEVICE_OUT_DEFAULT = 0x8000;
+ public static final int DEVICE_OUT_ALL = (DEVICE_OUT_EARPIECE |
+ DEVICE_OUT_SPEAKER |
+ DEVICE_OUT_WIRED_HEADSET |
+ DEVICE_OUT_WIRED_HEADPHONE |
+ DEVICE_OUT_BLUETOOTH_SCO |
+ DEVICE_OUT_BLUETOOTH_SCO_HEADSET |
+ DEVICE_OUT_BLUETOOTH_SCO_CARKIT |
+ DEVICE_OUT_BLUETOOTH_A2DP |
+ DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
+ DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER |
+ DEVICE_OUT_AUX_DIGITAL |
+ DEVICE_OUT_ANLG_DOCK_HEADSET |
+ DEVICE_OUT_DGTL_DOCK_HEADSET |
+ DEVICE_OUT_DEFAULT);
+ public static final int DEVICE_OUT_ALL_A2DP = (DEVICE_OUT_BLUETOOTH_A2DP |
+ DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
+ DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER);
// input devices
public static final int DEVICE_IN_COMMUNICATION = 0x10000;
public static final int DEVICE_IN_AMBIENT = 0x20000;
@@ -218,6 +236,54 @@ public class AudioSystem
public static final int DEVICE_STATE_AVAILABLE = 1;
private static final int NUM_DEVICE_STATES = 1;
+ public static final String DEVICE_OUT_EARPIECE_NAME = "earpiece";
+ public static final String DEVICE_OUT_SPEAKER_NAME = "speaker";
+ public static final String DEVICE_OUT_WIRED_HEADSET_NAME = "headset";
+ public static final String DEVICE_OUT_WIRED_HEADPHONE_NAME = "headphone";
+ public static final String DEVICE_OUT_BLUETOOTH_SCO_NAME = "bt_sco";
+ public static final String DEVICE_OUT_BLUETOOTH_SCO_HEADSET_NAME = "bt_sco_hs";
+ public static final String DEVICE_OUT_BLUETOOTH_SCO_CARKIT_NAME = "bt_sco_carkit";
+ public static final String DEVICE_OUT_BLUETOOTH_A2DP_NAME = "bt_a2dp";
+ public static final String DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES_NAME = "bt_a2dp_hp";
+ public static final String DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER_NAME = "bt_a2dp_spk";
+ public static final String DEVICE_OUT_AUX_DIGITAL_NAME = "aux_digital";
+ public static final String DEVICE_OUT_ANLG_DOCK_HEADSET_NAME = "analog_dock";
+ public static final String DEVICE_OUT_DGTL_DOCK_HEADSET_NAME = "digital_dock";
+
+ public static String getDeviceName(int device)
+ {
+ switch(device) {
+ case DEVICE_OUT_EARPIECE:
+ return DEVICE_OUT_EARPIECE_NAME;
+ case DEVICE_OUT_SPEAKER:
+ return DEVICE_OUT_SPEAKER_NAME;
+ case DEVICE_OUT_WIRED_HEADSET:
+ return DEVICE_OUT_WIRED_HEADSET_NAME;
+ case DEVICE_OUT_WIRED_HEADPHONE:
+ return DEVICE_OUT_WIRED_HEADPHONE_NAME;
+ case DEVICE_OUT_BLUETOOTH_SCO:
+ return DEVICE_OUT_BLUETOOTH_SCO_NAME;
+ case DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
+ return DEVICE_OUT_BLUETOOTH_SCO_HEADSET_NAME;
+ case DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
+ return DEVICE_OUT_BLUETOOTH_SCO_CARKIT_NAME;
+ case DEVICE_OUT_BLUETOOTH_A2DP:
+ return DEVICE_OUT_BLUETOOTH_A2DP_NAME;
+ case DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES:
+ return DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES_NAME;
+ case DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER:
+ return DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER_NAME;
+ case DEVICE_OUT_AUX_DIGITAL:
+ return DEVICE_OUT_AUX_DIGITAL_NAME;
+ case DEVICE_OUT_ANLG_DOCK_HEADSET:
+ return DEVICE_OUT_ANLG_DOCK_HEADSET_NAME;
+ case DEVICE_OUT_DGTL_DOCK_HEADSET:
+ return DEVICE_OUT_DGTL_DOCK_HEADSET_NAME;
+ default:
+ return "";
+ }
+ }
+
// phone state, match audio_mode???
public static final int PHONE_STATE_OFFCALL = 0;
public static final int PHONE_STATE_RINGING = 1;
@@ -247,11 +313,10 @@ public class AudioSystem
public static native int setDeviceConnectionState(int device, int state, String device_address);
public static native int getDeviceConnectionState(int device, String device_address);
public static native int setPhoneState(int state);
- public static native int setRingerMode(int mode, int mask);
public static native int setForceUse(int usage, int config);
public static native int getForceUse(int usage);
public static native int initStreamVolume(int stream, int indexMin, int indexMax);
- public static native int setStreamVolumeIndex(int stream, int index);
- public static native int getStreamVolumeIndex(int stream);
+ public static native int setStreamVolumeIndex(int stream, int index, int device);
+ public static native int getStreamVolumeIndex(int stream, int device);
public static native int getDevicesForStream(int stream);
}
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 4f9eb2b..7d4c282 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -29,7 +29,7 @@ import android.util.Log;
/**
* The AudioTrack class manages and plays a single audio resource for Java applications.
- * It allows to stream PCM audio buffers to the audio hardware for playback. This is
+ * It allows streaming 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.
*
@@ -46,7 +46,7 @@ import android.util.Log;
* <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
+ * The static mode should be chosen when dealing with short sounds that fit in memory and
* 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.
@@ -57,7 +57,7 @@ import android.util.Log;
* For an AudioTrack using the static mode, this size is the maximum size of the sound that can
* be played from it.<br>
* For the streaming mode, data will be written to the hardware in chunks of
- * sizes inferior to the total buffer size.
+ * sizes less than or equal to the total buffer size.
*/
public class AudioTrack
{
@@ -76,6 +76,7 @@ public class AudioTrack
/** indicates AudioTrack state is playing */
public static final int PLAYSTATE_PLAYING = 3; // matches SL_PLAYSTATE_PLAYING
+ // keep these values in sync with android_media_AudioTrack.cpp
/**
* Creation mode where audio data is transferred from Java to the native layer
* only once before the audio starts playing.
@@ -180,7 +181,7 @@ public class AudioTrack
/**
* The audio data sampling rate in Hz.
*/
- private int mSampleRate = 22050;
+ private int mSampleRate; // initialized by all constructors
/**
* The number of audio output channels (1 is mono, 2 is stereo).
*/
@@ -193,8 +194,9 @@ public class AudioTrack
/**
* The type of the audio stream to play. See
* {@link AudioManager#STREAM_VOICE_CALL}, {@link AudioManager#STREAM_SYSTEM},
- * {@link AudioManager#STREAM_RING}, {@link AudioManager#STREAM_MUSIC} and
- * {@link AudioManager#STREAM_ALARM}
+ * {@link AudioManager#STREAM_RING}, {@link AudioManager#STREAM_MUSIC},
+ * {@link AudioManager#STREAM_ALARM}, {@link AudioManager#STREAM_NOTIFICATION}, and
+ * {@link AudioManager#STREAM_DTMF}.
*/
private int mStreamType = AudioManager.STREAM_MUSIC;
/**
@@ -240,10 +242,9 @@ public class AudioTrack
* Class constructor.
* @param streamType the type of the audio stream. See
* {@link AudioManager#STREAM_VOICE_CALL}, {@link AudioManager#STREAM_SYSTEM},
- * {@link AudioManager#STREAM_RING}, {@link AudioManager#STREAM_MUSIC} and
- * {@link AudioManager#STREAM_ALARM}
- * @param sampleRateInHz the sample rate expressed in Hertz. Examples of rates are (but
- * not limited to) 44100, 22050 and 11025.
+ * {@link AudioManager#STREAM_RING}, {@link AudioManager#STREAM_MUSIC},
+ * {@link AudioManager#STREAM_ALARM}, and {@link AudioManager#STREAM_NOTIFICATION}.
+ * @param sampleRateInHz the sample rate expressed in Hertz.
* @param channelConfig describes the configuration of the audio channels.
* See {@link AudioFormat#CHANNEL_OUT_MONO} and
* {@link AudioFormat#CHANNEL_OUT_STEREO}
@@ -275,14 +276,15 @@ public class AudioTrack
* and media players in the same session and not to the output mix.
* When an AudioTrack is created without specifying a session, it will create its own session
* which can be retreived by calling the {@link #getAudioSessionId()} method.
- * If a session ID is provided, this AudioTrack will share effects attached to this session
- * with all other media players or audio tracks in the same session.
+ * If a non-zero session ID is provided, this AudioTrack will share effects attached to this
+ * session
+ * with all other media players or audio tracks in the same session, otherwise a new session
+ * will be created for this track if none is supplied.
* @param streamType the type of the audio stream. See
* {@link AudioManager#STREAM_VOICE_CALL}, {@link AudioManager#STREAM_SYSTEM},
- * {@link AudioManager#STREAM_RING}, {@link AudioManager#STREAM_MUSIC} and
- * {@link AudioManager#STREAM_ALARM}
- * @param sampleRateInHz the sample rate expressed in Hertz. Examples of rates are (but
- * not limited to) 44100, 22050 and 11025.
+ * {@link AudioManager#STREAM_RING}, {@link AudioManager#STREAM_MUSIC},
+ * {@link AudioManager#STREAM_ALARM}, and {@link AudioManager#STREAM_NOTIFICATION}.
+ * @param sampleRateInHz the sample rate expressed in Hertz.
* @param channelConfig describes the configuration of the audio channels.
* See {@link AudioFormat#CHANNEL_OUT_MONO} and
* {@link AudioFormat#CHANNEL_OUT_STEREO}
@@ -304,8 +306,8 @@ public class AudioTrack
int bufferSizeInBytes, int mode, int sessionId)
throws IllegalArgumentException {
mState = STATE_UNINITIALIZED;
-
- // remember which looper is associated with the AudioTrack instanciation
+
+ // remember which looper is associated with the AudioTrack instantiation
if ((mInitializationLooper = Looper.myLooper()) == null) {
mInitializationLooper = Looper.getMainLooper();
}
@@ -365,7 +367,7 @@ public class AudioTrack
}
//--------------
- // sample rate
+ // sample rate, note these values are subject to change
if ( (sampleRateInHz < 4000) || (sampleRateInHz > 48000) ) {
throw (new IllegalArgumentException(sampleRateInHz
+ "Hz is not a supported sample rate."));
@@ -449,7 +451,7 @@ public class AudioTrack
// AudioTrack subclasses too.
try {
stop();
- } catch(IllegalStateException ise) {
+ } catch(IllegalStateException ise) {
// don't raise an exception, we're releasing the resources.
}
native_release();
@@ -488,7 +490,7 @@ public class AudioTrack
public int getSampleRate() {
return mSampleRate;
}
-
+
/**
* Returns the current playback rate in Hz.
*/
@@ -508,7 +510,8 @@ public class AudioTrack
* Returns the type of audio stream this AudioTrack is configured for.
* Compare the result against {@link AudioManager#STREAM_VOICE_CALL},
* {@link AudioManager#STREAM_SYSTEM}, {@link AudioManager#STREAM_RING},
- * {@link AudioManager#STREAM_MUSIC} or {@link AudioManager#STREAM_ALARM}
+ * {@link AudioManager#STREAM_MUSIC}, {@link AudioManager#STREAM_ALARM},
+ * {@link AudioManager#STREAM_NOTIFICATION}, or {@link AudioManager#STREAM_DTMF}.
*/
public int getStreamType() {
return mStreamType;
@@ -590,22 +593,22 @@ public class AudioTrack
static public int getNativeOutputSampleRate(int streamType) {
return native_get_output_sample_rate(streamType);
}
-
+
/**
* Returns the minimum buffer size required for the successful creation of an AudioTrack
* object to be created in the {@link #MODE_STREAM} mode. Note that this size doesn't
* guarantee a smooth playback under load, and higher values should be chosen according to
- * the expected frequency at which the buffer will be refilled with additional data to play.
+ * the expected frequency at which the buffer will be refilled with additional data to play.
* @param sampleRateInHz the sample rate expressed in Hertz.
- * @param channelConfig describes the configuration of the audio channels.
+ * @param channelConfig describes the configuration of the audio channels.
* See {@link AudioFormat#CHANNEL_OUT_MONO} and
* {@link AudioFormat#CHANNEL_OUT_STEREO}
- * @param audioFormat the format in which the audio data is represented.
- * See {@link AudioFormat#ENCODING_PCM_16BIT} and
+ * @param audioFormat the format in which the audio data is represented.
+ * See {@link AudioFormat#ENCODING_PCM_16BIT} and
* {@link AudioFormat#ENCODING_PCM_8BIT}
* @return {@link #ERROR_BAD_VALUE} if an invalid parameter was passed,
- * or {@link #ERROR} if the implementation was unable to query the hardware for its output
- * properties,
+ * or {@link #ERROR} if the implementation was unable to query the hardware for its output
+ * properties,
* or the minimum buffer size expressed in bytes.
*/
static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) {
@@ -623,18 +626,19 @@ public class AudioTrack
loge("getMinBufferSize(): Invalid channel configuration.");
return AudioTrack.ERROR_BAD_VALUE;
}
-
- if ((audioFormat != AudioFormat.ENCODING_PCM_16BIT)
+
+ if ((audioFormat != AudioFormat.ENCODING_PCM_16BIT)
&& (audioFormat != AudioFormat.ENCODING_PCM_8BIT)) {
loge("getMinBufferSize(): Invalid audio format.");
return AudioTrack.ERROR_BAD_VALUE;
}
-
+
+ // sample rate, note these values are subject to change
if ( (sampleRateInHz < 4000) || (sampleRateInHz > 48000) ) {
loge("getMinBufferSize(): " + sampleRateInHz +"Hz is not a supported sample rate.");
return AudioTrack.ERROR_BAD_VALUE;
}
-
+
int size = native_get_min_buff_size(sampleRateInHz, channelCount, audioFormat);
if ((size == -1) || (size == 0)) {
loge("getMinBufferSize(): error querying hardware");
@@ -667,7 +671,7 @@ public class AudioTrack
public void setPlaybackPositionUpdateListener(OnPlaybackPositionUpdateListener listener) {
setPlaybackPositionUpdateListener(listener, null);
}
-
+
/**
* Sets the listener the AudioTrack notifies when a previously set marker is reached or
* for each periodic playback head position update.
@@ -676,7 +680,7 @@ public class AudioTrack
* @param listener
* @param handler the Handler that will receive the event notification messages.
*/
- public void setPlaybackPositionUpdateListener(OnPlaybackPositionUpdateListener listener,
+ public void setPlaybackPositionUpdateListener(OnPlaybackPositionUpdateListener listener,
Handler handler) {
synchronized (mPositionListenerLock) {
mPositionListener = listener;
@@ -684,7 +688,7 @@ public class AudioTrack
if (listener != null) {
mEventHandlerDelegate = new NativeEventHandlerDelegate(this, handler);
}
-
+
}
@@ -728,7 +732,7 @@ public class AudioTrack
* the audio data will be consumed and played back, not the original sampling rate of the
* content. Setting it to half the sample rate of the content will cause the playback to
* last twice as long, but will also result in a negative pitch shift.
- * The valid sample rate range if from 1Hz to twice the value returned by
+ * The valid sample rate range is from 1Hz to twice the value returned by
* {@link #getNativeOutputSampleRate(int)}.
* @param sampleRateInHz the sample rate expressed in Hz
* @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE},
@@ -836,7 +840,10 @@ public class AudioTrack
/**
* Stops playing the audio data.
- *
+ * When used on an instance created in {@link #MODE_STREAM} mode, audio will stop playing
+ * after the last buffer that was written has been played. For an immediate stop, use
+ * {@link #pause()}, followed by {@link #flush()} to discard audio data that hasn't been played
+ * back yet.
* @throws IllegalStateException
*/
public void stop()
@@ -855,7 +862,7 @@ public class AudioTrack
/**
* 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.
+ * this data back. See {@link #flush()} to discard this data.
*
* @throws IllegalStateException
*/
@@ -906,7 +913,7 @@ public class AudioTrack
* the parameters don't resolve to valid data and indexes.
*/
- public int write(byte[] audioData,int offsetInBytes, int sizeInBytes) {
+ public int write(byte[] audioData, int offsetInBytes, int sizeInBytes) {
if ((mDataLoadMode == MODE_STATIC)
&& (mState == STATE_NO_STATIC_DATA)
&& (sizeInBytes > 0)) {
@@ -917,7 +924,7 @@ public class AudioTrack
return ERROR_INVALID_OPERATION;
}
- if ( (audioData == null) || (offsetInBytes < 0 ) || (sizeInBytes < 0)
+ if ( (audioData == null) || (offsetInBytes < 0 ) || (sizeInBytes < 0)
|| (offsetInBytes + sizeInBytes > audioData.length)) {
return ERROR_BAD_VALUE;
}
@@ -948,12 +955,12 @@ public class AudioTrack
&& (sizeInShorts > 0)) {
mState = STATE_INITIALIZED;
}
-
+
if (mState != STATE_INITIALIZED) {
return ERROR_INVALID_OPERATION;
}
- if ( (audioData == null) || (offsetInShorts < 0 ) || (sizeInShorts < 0)
+ if ( (audioData == null) || (offsetInShorts < 0 ) || (sizeInShorts < 0)
|| (offsetInShorts + sizeInShorts > audioData.length)) {
return ERROR_BAD_VALUE;
}
@@ -1012,8 +1019,8 @@ public class AudioTrack
* <p>Note that the passed level value is a raw scalar. UI controls should be scaled
* logarithmically: the gain applied by audio framework ranges from -72dB to 0dB,
* so an appropriate conversion from linear UI input x to level is:
- * x == 0 -> level = 0
- * 0 < x <= R -> level = 10^(72*(x-R)/20/R)
+ * x == 0 -&gt; level = 0
+ * 0 &lt; x &lt;= R -&gt; level = 10^(72*(x-R)/20/R)
*
* @param level send level scalar
* @return error code or success, see {@link #SUCCESS},
@@ -1047,7 +1054,7 @@ public class AudioTrack
* by the playback head.
*/
void onMarkerReached(AudioTrack track);
-
+
/**
* Called on the listener to periodically notify it that the playback head has reached
* a multiple of the notification period.
@@ -1062,11 +1069,11 @@ public class AudioTrack
/**
* Helper class to handle the forwarding of native events to the appropriate listener
* (potentially) handled in a different thread
- */
+ */
private class NativeEventHandlerDelegate {
private final AudioTrack mAudioTrack;
private final Handler mHandler;
-
+
NativeEventHandlerDelegate(AudioTrack track, Handler handler) {
mAudioTrack = track;
// find the looper for our new event handler
@@ -1077,7 +1084,7 @@ public class AudioTrack
// no given handler, use the looper the AudioTrack was created in
looper = mInitializationLooper;
}
-
+
// construct the event handler with this looper
if (looper != null) {
// implement the event handler delegate
@@ -1111,9 +1118,9 @@ public class AudioTrack
};
} else {
mHandler = null;
- }
+ }
}
-
+
Handler getHandler() {
return mHandler;
}
@@ -1133,7 +1140,7 @@ public class AudioTrack
}
if (track.mEventHandlerDelegate != null) {
- Message m =
+ Message m =
track.mEventHandlerDelegate.getHandler().obtainMessage(what, arg1, arg2, obj);
track.mEventHandlerDelegate.getHandler().sendMessage(m);
}
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 925f965..9d6c9f6 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -111,7 +111,7 @@ public class ExifInterface {
// there can only be one user at a time for the native functions (and
// they cannot keep state in the native code across function calls). We
// use sLock to serialize the accesses.
- private static Object sLock = new Object();
+ private static final Object sLock = new Object();
/**
* Reads Exif tags from the specified JPEG file.
diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java
index e275aa6..7f7e284 100644
--- a/media/java/android/media/MediaFile.java
+++ b/media/java/android/media/MediaFile.java
@@ -120,18 +120,18 @@ public class MediaFile {
}
}
- private static HashMap<String, MediaFileType> sFileTypeMap
+ private static final HashMap<String, MediaFileType> sFileTypeMap
= new HashMap<String, MediaFileType>();
- private static HashMap<String, Integer> sMimeTypeMap
+ private static final HashMap<String, Integer> sMimeTypeMap
= new HashMap<String, Integer>();
// maps file extension to MTP format code
- private static HashMap<String, Integer> sFileTypeToFormatMap
+ private static final HashMap<String, Integer> sFileTypeToFormatMap
= new HashMap<String, Integer>();
// maps mime type to MTP format code
- private static HashMap<String, Integer> sMimeTypeToFormatMap
+ private static final HashMap<String, Integer> sMimeTypeToFormatMap
= new HashMap<String, Integer>();
// maps MTP format code to mime type
- private static HashMap<Integer, String> sFormatToMimeTypeMap
+ private static final HashMap<Integer, String> sFormatToMimeTypeMap
= new HashMap<Integer, String>();
static void addFileType(String extension, int fileType, String mimeType) {
diff --git a/media/java/android/media/MediaInserter.java b/media/java/android/media/MediaInserter.java
index a998407..e92c710 100644
--- a/media/java/android/media/MediaInserter.java
+++ b/media/java/android/media/MediaInserter.java
@@ -32,7 +32,7 @@ import java.util.List;
* {@hide}
*/
public class MediaInserter {
- private HashMap<Uri, List<ContentValues>> mRowMap =
+ private final HashMap<Uri, List<ContentValues>> mRowMap =
new HashMap<Uri, List<ContentValues>>();
private IContentProvider mProvider;
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 8d71dcf..4c70e9d 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1735,6 +1735,9 @@ public class MediaPlayer
/**
* Called to indicate the video size
*
+ * The video size (width and height) could be 0 if there was no video,
+ * no display surface was set, or the value was not determined yet.
+ *
* @param mp the MediaPlayer associated with this callback
* @param width the width of the video
* @param height the height of the video
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 08e6032..85d99c1 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -138,10 +138,13 @@ public class MediaRecorder
*/
public final class AudioSource {
/* Do not change these values without updating their counterparts
- * in include/media/mediarecorder.h!
+ * in system/core/include/system/audio.h!
*/
private AudioSource() {}
+
+ /** Default audio source **/
public static final int DEFAULT = 0;
+
/** Microphone audio source */
public static final int MIC = 1;
@@ -201,18 +204,24 @@ public class MediaRecorder
/** MPEG4 media file format*/
public static final int MPEG_4 = 2;
- /** The following formats are audio only .aac or .amr formats **/
- /** @deprecated Deprecated in favor of AMR_NB */
- /** Deprecated in favor of MediaRecorder.OutputFormat.AMR_NB */
- /** AMR NB file format */
+ /** The following formats are audio only .aac or .amr formats */
+
+ /**
+ * AMR NB file format
+ * @deprecated Deprecated in favor of MediaRecorder.OutputFormat.AMR_NB
+ */
public static final int RAW_AMR = 3;
+
/** AMR NB file format */
public static final int AMR_NB = 3;
+
/** AMR WB file format */
public static final int AMR_WB = 4;
+
/** @hide AAC ADIF file format */
public static final int AAC_ADIF = 5;
- /** @hide AAC ADTS file format */
+
+ /** AAC ADTS file format */
public static final int AAC_ADTS = 6;
/** @hide Stream over a socket, limited to a single stream */
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 386986e..1c13fff 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -35,6 +35,7 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.provider.MediaStore;
+import android.provider.MediaStore.Files.FileColumns;
import android.provider.Settings;
import android.provider.MediaStore.Audio;
import android.provider.MediaStore.Files;
@@ -58,6 +59,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.Locale;
/**
* Internal service helper that no-one should use directly.
@@ -312,17 +314,8 @@ public class MediaScanner
private final String mExternalStoragePath;
- // WARNING: Bulk inserts sounded like a great idea and gave us a good performance improvement,
- // but unfortunately it also introduced a number of bugs. Many of those bugs were fixed,
- // but (at least) one problem is still outstanding:
- //
- // - Bulk inserts broke the code that sets the default ringtones, notifications, and alarms
- // on first boot
- //
- // This problem might be solvable by moving the logic to the media provider or disabling bulk
- // inserts only for those cases. For now, we are disabling bulk inserts until we have a solid
- // fix for this problem.
- private static final boolean ENABLE_BULK_INSERTS = false;
+ /** 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
@@ -352,7 +345,7 @@ public class MediaScanner
// this should be set when scanning files on a case insensitive file system.
private boolean mCaseInsensitivePaths;
- private BitmapFactory.Options mBitmapOptions = new BitmapFactory.Options();
+ private final BitmapFactory.Options mBitmapOptions = new BitmapFactory.Options();
private static class FileCacheEntry {
long mRowId;
@@ -396,6 +389,7 @@ public class MediaScanner
setDefaultRingtoneFileNames();
mExternalStoragePath = Environment.getExternalStorageDirectory().getAbsolutePath();
+ //mClient.testGenreNameConverter();
}
private void setDefaultRingtoneFileNames() {
@@ -407,7 +401,7 @@ public class MediaScanner
+ Settings.System.ALARM_ALERT);
}
- private MyMediaScannerClient mClient = new MyMediaScannerClient();
+ private final MyMediaScannerClient mClient = new MyMediaScannerClient();
private boolean isDrmEnabled() {
String prop = SystemProperties.get("drm.service.enabled");
@@ -623,8 +617,36 @@ public class MediaScanner
mCompilation = parseSubstring(value, 0, 0);
} else if (name.equalsIgnoreCase("isdrm")) {
mIsDrm = (parseSubstring(value, 0, 0) == 1);
+ } else {
+ //Log.v(TAG, "unknown tag: " + name + " (" + mProcessGenres + ")");
+ }
+ }
+
+ 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) {
@@ -633,18 +655,23 @@ public class MediaScanner
}
final int length = genreTagValue.length();
- if (length > 0 && genreTagValue.charAt(0) == '(') {
+ if (length > 0) {
+ boolean parenthesized = false;
StringBuffer number = new StringBuffer();
- int i = 1;
- for (; i < length - 1; ++i) {
+ int i = 0;
+ for (; i < length; ++i) {
char c = genreTagValue.charAt(i);
- if (Character.isDigit(c)) {
+ if (i == 0 && c == '(') {
+ parenthesized = true;
+ } else if (Character.isDigit(c)) {
number.append(c);
} else {
break;
}
}
- if (genreTagValue.charAt(i) == ')') {
+ char charAfterNumber = i < length ? genreTagValue.charAt(i) : ' ';
+ if ((parenthesized && charAfterNumber == ')')
+ || !parenthesized && Character.isWhitespace(charAfterNumber)) {
try {
short genreIndex = Short.parseShort(number.toString());
if (genreIndex >= 0) {
@@ -655,7 +682,13 @@ public class MediaScanner
} else if (genreIndex < 0xFF && (i + 1) < length) {
// genre is valid but unknown,
// if there is a string after the value we take it
- return genreTagValue.substring(i + 1);
+ 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();
@@ -855,6 +888,7 @@ public class MediaScanner
}
}
Uri result = null;
+ boolean needToSetSettings = false;
if (rowId == 0) {
if (mMtpObjectHandle != 0) {
values.put(MediaStore.MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID, mMtpObjectHandle);
@@ -866,12 +900,37 @@ public class MediaScanner
}
values.put(Files.FileColumns.FORMAT, format);
}
+ // 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 (mWasEmptyPriorToScan) {
+ 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;
+ }
+ }
+ }
+
// new file, insert it
// We insert directories immediately to ensure they are in the database
// before the files they contain.
// Otherwise we can get duplicate directory entries in the database
// if one of the media FileInserters is flushed before the files table FileInserter
- if (inserter == null || entry.mFormat == MtpConstants.FORMAT_ASSOCIATION) {
+ // Also, we immediately insert the file if the rowId of the inserted file is
+ // needed.
+ if (inserter == null || needToSetSettings ||
+ entry.mFormat == MtpConstants.FORMAT_ASSOCIATION) {
result = mMediaProvider.insert(tableUri, values);
} else {
inserter.insert(tableUri, values);
@@ -887,24 +946,33 @@ public class MediaScanner
// path should never change, and we want to avoid replacing mixed cased paths
// with squashed lower case paths
values.remove(MediaStore.MediaColumns.DATA);
+
+ int mediaType = 0;
+ if (!MediaScanner.isNoMediaPath(entry.mPath)) {
+ int fileType = MediaFile.getFileTypeForMimeType(mMimeType);
+ if (MediaFile.isAudioFileType(fileType)) {
+ mediaType = FileColumns.MEDIA_TYPE_AUDIO;
+ } else if (MediaFile.isVideoFileType(fileType)) {
+ mediaType = FileColumns.MEDIA_TYPE_VIDEO;
+ } else if (MediaFile.isImageFileType(fileType)) {
+ mediaType = FileColumns.MEDIA_TYPE_IMAGE;
+ } else if (MediaFile.isPlayListFileType(fileType)) {
+ mediaType = FileColumns.MEDIA_TYPE_PLAYLIST;
+ }
+ values.put(FileColumns.MEDIA_TYPE, mediaType);
+ }
+
mMediaProvider.update(result, values, null, null);
}
- if (notifications && mWasEmptyPriorToScan && !mDefaultNotificationSet) {
- if (TextUtils.isEmpty(mDefaultNotificationFilename) ||
- doesPathHaveFilename(entry.mPath, mDefaultNotificationFilename)) {
+ if(needToSetSettings) {
+ if (notifications) {
setSettingIfNotSet(Settings.System.NOTIFICATION_SOUND, tableUri, rowId);
mDefaultNotificationSet = true;
- }
- } else if (ringtones && mWasEmptyPriorToScan && !mDefaultRingtoneSet) {
- if (TextUtils.isEmpty(mDefaultRingtoneFilename) ||
- doesPathHaveFilename(entry.mPath, mDefaultRingtoneFilename)) {
+ } else if (ringtones) {
setSettingIfNotSet(Settings.System.RINGTONE, tableUri, rowId);
mDefaultRingtoneSet = true;
- }
- } else if (alarms && mWasEmptyPriorToScan && !mDefaultAlarmSet) {
- if (TextUtils.isEmpty(mDefaultAlarmAlertFilename) ||
- doesPathHaveFilename(entry.mPath, mDefaultAlarmAlertFilename)) {
+ } else if (alarms) {
setSettingIfNotSet(Settings.System.ALARM_ALERT, tableUri, rowId);
mDefaultAlarmSet = true;
}
@@ -983,7 +1051,7 @@ public class MediaScanner
// First read existing files from the files table
c = mMediaProvider.query(mFilesUri, FILES_PRESCAN_PROJECTION,
- where, selectionArgs, null);
+ where, selectionArgs, null, null);
if (c != null) {
mWasEmptyPriorToScan = c.getCount() == 0;
@@ -1020,7 +1088,7 @@ public class MediaScanner
// compute original size of images
mOriginalCount = 0;
- c = mMediaProvider.query(mImagesUri, ID_PROJECTION, null, null, null);
+ c = mMediaProvider.query(mImagesUri, ID_PROJECTION, null, null, null, null);
if (c != null) {
mOriginalCount = c.getCount();
c.close();
@@ -1055,7 +1123,7 @@ public class MediaScanner
new String [] { "_data" },
null,
null,
- null);
+ null, null);
Log.v(TAG, "pruneDeadThumbnailFiles... " + c);
if (c != null && c.moveToFirst()) {
do {
@@ -1128,6 +1196,10 @@ public class MediaScanner
mMediaProvider.delete(ContentUris.withAppendedId(mFilesUri, entry.mRowId),
null, null);
iterator.remove();
+ if (entry.mPath.toLowerCase(Locale.US).endsWith("/.nomedia")) {
+ File f = new File(path);
+ mMediaProvider.call(MediaStore.UNHIDE_CALL, f.getParent(), null);
+ }
}
}
}
@@ -1420,7 +1492,7 @@ public class MediaScanner
if (bestMatch.mRowId == 0) {
Cursor c = mMediaProvider.query(mAudioUri, ID_PROJECTION,
MediaStore.Files.FileColumns.DATA + "=?",
- new String[] { bestMatch.mPath }, null);
+ new String[] { bestMatch.mPath }, null, null);
if (c != null) {
if (c.moveToNext()) {
bestMatch.mRowId = c.getLong(0);
diff --git a/media/java/android/media/MediaScannerConnection.java b/media/java/android/media/MediaScannerConnection.java
index 969da39..21b6e14 100644
--- a/media/java/android/media/MediaScannerConnection.java
+++ b/media/java/android/media/MediaScannerConnection.java
@@ -46,7 +46,7 @@ public class MediaScannerConnection implements ServiceConnection {
private IMediaScannerService mService;
private boolean mConnected; // true if connect() has been called since last disconnect()
- private IMediaScannerListener.Stub mListener = new IMediaScannerListener.Stub() {
+ private final IMediaScannerListener.Stub mListener = new IMediaScannerListener.Stub() {
public void scanCompleted(String path, Uri uri) {
MediaScannerConnectionClient client = mClient;
if (client != null) {
diff --git a/media/java/android/media/MiniThumbFile.java b/media/java/android/media/MiniThumbFile.java
index df141c1..63b149c 100644
--- a/media/java/android/media/MiniThumbFile.java
+++ b/media/java/android/media/MiniThumbFile.java
@@ -52,7 +52,7 @@ public class MiniThumbFile {
private RandomAccessFile mMiniThumbFile;
private FileChannel mChannel;
private ByteBuffer mBuffer;
- private static Hashtable<String, MiniThumbFile> sThumbFiles =
+ private static final Hashtable<String, MiniThumbFile> sThumbFiles =
new Hashtable<String, MiniThumbFile>();
/**
diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java
index 77acfe6..18b4ee6 100644
--- a/media/java/android/media/RemoteControlClient.java
+++ b/media/java/android/media/RemoteControlClient.java
@@ -576,6 +576,7 @@ public class RemoteControlClient
/**
* Cache for the metadata strings.
* Access synchronized on mCacheLock
+ * This is re-initialized in apply() and so cannot be final.
*/
private Bundle mMetadata = new Bundle();
@@ -621,7 +622,7 @@ public class RemoteControlClient
/**
* The IRemoteControlClient implementation
*/
- private IRemoteControlClient mIRCC = new IRemoteControlClient.Stub() {
+ private final IRemoteControlClient mIRCC = new IRemoteControlClient.Stub() {
public void onInformationRequested(int clientGeneration, int infoFlags,
int artWidth, int artHeight) {
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 9c0819f..7aaf4aa 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -224,7 +224,7 @@ public class RingtoneManager {
* If a column (item from this list) exists in the Cursor, its value must
* be true (value of 1) for the row to be returned.
*/
- private List<String> mFilterColumns = new ArrayList<String>();
+ private final List<String> mFilterColumns = new ArrayList<String>();
private boolean mStopPreviousRingtone = true;
private Ringtone mPreviousRingtone;
diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java
index 5e9c018..0f68e98 100644
--- a/media/java/android/media/SoundPool.java
+++ b/media/java/android/media/SoundPool.java
@@ -161,12 +161,10 @@ public class SoundPool
int id = 0;
try {
File f = new File(path);
- if (f != null) {
- ParcelFileDescriptor fd = ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY);
- if (fd != null) {
- id = _load(fd.getFileDescriptor(), 0, f.length(), priority);
- fd.close();
- }
+ ParcelFileDescriptor fd = ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY);
+ if (fd != null) {
+ id = _load(fd.getFileDescriptor(), 0, f.length(), priority);
+ fd.close();
}
} catch (java.io.IOException e) {
Log.e(TAG, "error loading " + path);
diff --git a/media/java/android/media/ThumbnailUtils.java b/media/java/android/media/ThumbnailUtils.java
index 078d4af..8eb9332 100644
--- a/media/java/android/media/ThumbnailUtils.java
+++ b/media/java/android/media/ThumbnailUtils.java
@@ -104,8 +104,10 @@ public class ThumbnailUtils {
}
if (bitmap == null) {
+ FileInputStream stream = null;
try {
- FileDescriptor fd = new FileInputStream(filePath).getFD();
+ stream = new FileInputStream(filePath);
+ FileDescriptor fd = stream.getFD();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 1;
options.inJustDecodeBounds = true;
@@ -125,7 +127,16 @@ public class ThumbnailUtils {
Log.e(TAG, "", ex);
} catch (OutOfMemoryError oom) {
Log.e(TAG, "Unable to decode file " + filePath + ". OutOfMemoryError.", oom);
+ } finally {
+ try {
+ if (stream != null) {
+ stream.close();
+ }
+ } catch (IOException ex) {
+ Log.e(TAG, "", ex);
+ }
}
+
}
if (kind == Images.Thumbnails.MICRO_KIND) {
@@ -472,9 +483,7 @@ public class ThumbnailUtils {
byte [] thumbData = null;
try {
exif = new ExifInterface(filePath);
- if (exif != null) {
- thumbData = exif.getThumbnail();
- }
+ thumbData = exif.getThumbnail();
} catch (IOException ex) {
Log.w(TAG, ex);
}
diff --git a/media/java/android/media/audiofx/AudioEffect.java b/media/java/android/media/audiofx/AudioEffect.java
index 673f9f4..85be267 100644
--- a/media/java/android/media/audiofx/AudioEffect.java
+++ b/media/java/android/media/audiofx/AudioEffect.java
@@ -386,7 +386,7 @@ public class AudioEffect {
default:
throw (new RuntimeException(
"Cannot initialize effect engine for type: " + type
- + "Error: " + initResult));
+ + " Error: " + initResult));
}
}
mId = id[0];
diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
index 19db1c0..18aa4b3 100755
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -266,7 +266,7 @@ public class MtpDatabase {
Cursor c = null;
try {
c = mMediaProvider.query(mObjectsUri, ID_PROJECTION, PATH_WHERE,
- new String[] { path }, null);
+ new String[] { path }, null, null);
if (c != null && c.getCount() > 0) {
Log.w(TAG, "file already exists in beginSendObject: " + path);
return -1;
@@ -433,7 +433,7 @@ public class MtpDatabase {
}
}
- return mMediaProvider.query(mObjectsUri, ID_PROJECTION, where, whereArgs, null);
+ return mMediaProvider.query(mObjectsUri, ID_PROJECTION, where, whereArgs, null, null);
}
private int[] getObjectList(int storageID, int format, int parent) {
@@ -699,7 +699,7 @@ public class MtpDatabase {
String path = null;
String[] whereArgs = new String[] { Integer.toString(handle) };
try {
- c = mMediaProvider.query(mObjectsUri, PATH_PROJECTION, ID_WHERE, whereArgs, null);
+ c = mMediaProvider.query(mObjectsUri, PATH_PROJECTION, ID_WHERE, whereArgs, null, null);
if (c != null && c.moveToNext()) {
path = c.getString(1);
}
@@ -752,6 +752,29 @@ public class MtpDatabase {
return MtpConstants.RESPONSE_GENERAL_ERROR;
}
+ // check if nomedia status changed
+ if (newFile.isDirectory()) {
+ // for directories, check if renamed from something hidden to something non-hidden
+ if (oldFile.getName().startsWith(".") && !newPath.startsWith(".")) {
+ // directory was unhidden
+ try {
+ mMediaProvider.call(MediaStore.UNHIDE_CALL, newPath, null);
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to unhide/rescan for " + newPath);
+ }
+ }
+ } else {
+ // for files, check if renamed from .nomedia to something else
+ if (oldFile.getName().toLowerCase(Locale.US).equals(".nomedia")
+ && !newPath.toLowerCase(Locale.US).equals(".nomedia")) {
+ try {
+ mMediaProvider.call(MediaStore.UNHIDE_CALL, oldFile.getParent(), null);
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to unhide/rescan for " + newPath);
+ }
+ }
+ }
+
return MtpConstants.RESPONSE_OK;
}
@@ -815,7 +838,7 @@ public class MtpDatabase {
Cursor c = null;
try {
c = mMediaProvider.query(mObjectsUri, OBJECT_INFO_PROJECTION,
- ID_WHERE, new String[] { Integer.toString(handle) }, null);
+ ID_WHERE, new String[] { Integer.toString(handle) }, null, null);
if (c != null && c.moveToNext()) {
outStorageFormatParent[0] = c.getInt(1);
outStorageFormatParent[1] = c.getInt(2);
@@ -858,7 +881,7 @@ public class MtpDatabase {
Cursor c = null;
try {
c = mMediaProvider.query(mObjectsUri, PATH_SIZE_FORMAT_PROJECTION,
- ID_WHERE, new String[] { Integer.toString(handle) }, null);
+ ID_WHERE, new String[] { Integer.toString(handle) }, null, null);
if (c != null && c.moveToNext()) {
String path = c.getString(1);
path.getChars(0, path.length(), outFilePath, 0);
@@ -887,7 +910,7 @@ public class MtpDatabase {
Cursor c = null;
try {
c = mMediaProvider.query(mObjectsUri, PATH_SIZE_FORMAT_PROJECTION,
- ID_WHERE, new String[] { Integer.toString(handle) }, null);
+ ID_WHERE, new String[] { Integer.toString(handle) }, null, null);
if (c != null && c.moveToNext()) {
// don't convert to media path here, since we will be matching
// against paths in the database matching /data/media
@@ -915,6 +938,15 @@ public class MtpDatabase {
Uri uri = Files.getMtpObjectsUri(mVolumeName, handle);
if (mMediaProvider.delete(uri, null, null) > 0) {
+ if (format != MtpConstants.FORMAT_ASSOCIATION
+ && path.toLowerCase(Locale.US).endsWith("/.nomedia")) {
+ try {
+ String parentPath = path.substring(0, path.lastIndexOf("/"));
+ mMediaProvider.call(MediaStore.UNHIDE_CALL, parentPath, null);
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to unhide/rescan for " + path);
+ }
+ }
return MtpConstants.RESPONSE_OK;
} else {
return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE;
@@ -933,7 +965,7 @@ public class MtpDatabase {
Uri uri = Files.getMtpReferencesUri(mVolumeName, handle);
Cursor c = null;
try {
- c = mMediaProvider.query(uri, ID_PROJECTION, null, null, null);
+ c = mMediaProvider.query(uri, ID_PROJECTION, null, null, null, null);
if (c == null) {
return null;
}
diff --git a/media/java/android/mtp/MtpPropertyGroup.java b/media/java/android/mtp/MtpPropertyGroup.java
index 76c8569..dab5454 100644
--- a/media/java/android/mtp/MtpPropertyGroup.java
+++ b/media/java/android/mtp/MtpPropertyGroup.java
@@ -191,7 +191,7 @@ class MtpPropertyGroup {
// for now we are only reading properties from the "objects" table
c = mProvider.query(mUri,
new String [] { Files.FileColumns._ID, column },
- ID_WHERE, new String[] { Integer.toString(id) }, null);
+ ID_WHERE, new String[] { Integer.toString(id) }, null, null);
if (c != null && c.moveToNext()) {
return c.getString(1);
} else {
@@ -211,7 +211,7 @@ class MtpPropertyGroup {
try {
c = mProvider.query(Audio.Media.getContentUri(mVolumeName),
new String [] { Files.FileColumns._ID, column },
- ID_WHERE, new String[] { Integer.toString(id) }, null);
+ ID_WHERE, new String[] { Integer.toString(id) }, null, null);
if (c != null && c.moveToNext()) {
return c.getString(1);
} else {
@@ -232,7 +232,7 @@ class MtpPropertyGroup {
Uri uri = Audio.Genres.getContentUriForAudioId(mVolumeName, id);
c = mProvider.query(uri,
new String [] { Files.FileColumns._ID, Audio.GenresColumns.NAME },
- null, null, null);
+ null, null, null, null);
if (c != null && c.moveToNext()) {
return c.getString(1);
} else {
@@ -254,7 +254,7 @@ class MtpPropertyGroup {
// for now we are only reading properties from the "objects" table
c = mProvider.query(mUri,
new String [] { Files.FileColumns._ID, column },
- ID_WHERE, new String[] { Integer.toString(id) }, null);
+ ID_WHERE, new String[] { Integer.toString(id) }, null, null);
if (c != null && c.moveToNext()) {
return new Long(c.getLong(1));
}
@@ -323,7 +323,7 @@ class MtpPropertyGroup {
try {
// don't query if not necessary
if (depth > 0 || handle == 0xFFFFFFFF || mColumns.length > 1) {
- c = mProvider.query(mUri, mColumns, where, whereArgs, null);
+ c = mProvider.query(mUri, mColumns, where, whereArgs, null, null);
if (c == null) {
return new MtpPropertyList(0, MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE);
}