diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index b5e3af07b7e60..43fb4b9d87a2c 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -1513,9 +1513,9 @@ public class AudioTrack extends PlayerBase
}
@Override
- void playerSetVolume(float leftVolume, float rightVolume) {
- leftVolume = clampGainOrLevel(leftVolume);
- rightVolume = clampGainOrLevel(rightVolume);
+ void playerSetVolume(boolean muting, float leftVolume, float rightVolume) {
+ leftVolume = clampGainOrLevel(muting ? 0.0f : leftVolume);
+ rightVolume = clampGainOrLevel(muting ? 0.0f : rightVolume);
native_setVolume(leftVolume, rightVolume);
}
@@ -2393,8 +2393,8 @@ public class AudioTrack extends PlayerBase
}
@Override
- int playerSetAuxEffectSendLevel(float level) {
- level = clampGainOrLevel(level);
+ int playerSetAuxEffectSendLevel(boolean muting, float level) {
+ level = clampGainOrLevel(muting ? 0.0f : level);
int err = native_setAuxEffectSendLevel(level);
return err == 0 ? SUCCESS : ERROR;
}
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 82cf965a75fb0..36ad90b6026cd 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1826,8 +1826,8 @@ public class MediaPlayer extends PlayerBase
}
@Override
- void playerSetVolume(float leftVolume, float rightVolume) {
- _setVolume(leftVolume, rightVolume);
+ void playerSetVolume(boolean muting, float leftVolume, float rightVolume) {
+ _setVolume(muting ? 0.0f : leftVolume, muting ? 0.0f : rightVolume);
}
private native void _setVolume(float leftVolume, float rightVolume);
@@ -1900,8 +1900,8 @@ public class MediaPlayer extends PlayerBase
}
@Override
- int playerSetAuxEffectSendLevel(float level) {
- _setAuxEffectSendLevel(level);
+ int playerSetAuxEffectSendLevel(boolean muting, float level) {
+ _setAuxEffectSendLevel(muting ? 0.0f : level);
return AudioSystem.SUCCESS;
}
diff --git a/media/java/android/media/PlayerBase.java b/media/java/android/media/PlayerBase.java
index b262d97cfb8e1..690a553849e32 100644
--- a/media/java/android/media/PlayerBase.java
+++ b/media/java/android/media/PlayerBase.java
@@ -39,6 +39,11 @@ import com.android.internal.app.IAppOpsService;
*/
public abstract class PlayerBase {
+ private final static String TAG = "PlayerBase";
+ private static IAudioService sService; //lazy initialization, use getService()
+ /** Debug app ops */
+ protected static final boolean DEBUG_APP_OPS = Log.isLoggable(TAG + ".AO", Log.DEBUG);
+
// parameters of the player that affect AppOps
protected AudioAttributes mAttributes;
protected float mLeftVolume = 1.0f;
@@ -51,7 +56,6 @@ public abstract class PlayerBase {
private boolean mHasAppOpsPlayAudio = true;
private final Object mAppOpsLock = new Object();
-
/**
* Constructor. Must be given audio attributes, as they are required for AppOps.
* @param attr non-null audio attributes
@@ -101,7 +105,7 @@ public abstract class PlayerBase {
void baseStart() {
synchronized (mAppOpsLock) {
if (isRestricted_sync()) {
- playerSetVolume(0, 0);
+ playerSetVolume(true/*muting*/,0, 0);
}
}
}
@@ -114,7 +118,7 @@ public abstract class PlayerBase {
return;
}
}
- playerSetVolume(leftVolume, rightVolume);
+ playerSetVolume(false/*muting*/,leftVolume, rightVolume);
}
int baseSetAuxEffectSendLevel(float level) {
@@ -124,7 +128,7 @@ public abstract class PlayerBase {
return AudioSystem.SUCCESS;
}
}
- return playerSetAuxEffectSendLevel(level);
+ return playerSetAuxEffectSendLevel(false/*muting*/, level);
}
/**
@@ -159,11 +163,18 @@ public abstract class PlayerBase {
try {
if (oldHasAppOpsPlayAudio != mHasAppOpsPlayAudio) {
if (mHasAppOpsPlayAudio) {
- playerSetVolume(mLeftVolume, mRightVolume);
- playerSetAuxEffectSendLevel(mAuxEffectSendLevel);
+ if (DEBUG_APP_OPS) {
+ Log.v(TAG, "updateAppOpsPlayAudio: unmuting player, vol=" + mLeftVolume
+ + "/" + mRightVolume);
+ }
+ playerSetVolume(false/*muting*/, mLeftVolume, mRightVolume);
+ playerSetAuxEffectSendLevel(false/*muting*/, mAuxEffectSendLevel);
} else {
- playerSetVolume(0.0f, 0.0f);
- playerSetAuxEffectSendLevel(0.0f);
+ if (DEBUG_APP_OPS) {
+ Log.v(TAG, "updateAppOpsPlayAudio: muting player");
+ }
+ playerSetVolume(true/*muting*/, 0.0f, 0.0f);
+ playerSetAuxEffectSendLevel(true/*muting*/, 0.0f);
}
}
} catch (Exception e) {
@@ -171,7 +182,6 @@ public abstract class PlayerBase {
}
}
-
/**
* To be called by the subclass whenever an operation is potentially restricted.
* As the media player-common behavior are incorporated into this class, the subclass's need
@@ -189,10 +199,41 @@ public abstract class PlayerBase {
if ((mAttributes.getAllFlags() & AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY) != 0) {
return false;
}
+ // check force audibility flag and camera restriction
+ if (((mAttributes.getAllFlags() & AudioAttributes.FLAG_AUDIBILITY_ENFORCED) != 0)
+ && (mAttributes.getUsage() == AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)) {
+ boolean cameraSoundForced = false;
+ try {
+ cameraSoundForced = getService().isCameraSoundForced();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Cannot access AudioService in isRestricted_sync()");
+ } catch (NullPointerException e) {
+ Log.e(TAG, "Null AudioService in isRestricted_sync()");
+ }
+ if (cameraSoundForced) {
+ return false;
+ }
+ }
return true;
}
+ private static IAudioService getService()
+ {
+ if (sService != null) {
+ return sService;
+ }
+ IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
+ sService = IAudioService.Stub.asInterface(b);
+ return sService;
+ }
+
// Abstract methods a subclass needs to implement
- abstract void playerSetVolume(float leftVolume, float rightVolume);
- abstract int playerSetAuxEffectSendLevel(float level);
+ /**
+ * Abstract method for the subclass behavior's for volume and muting commands
+ * @param muting if true, the player is to be muted, and the volume values can be ignored
+ * @param leftVolume the left volume to use if muting is false
+ * @param rightVolume the right volume to use if muting is false
+ */
+ abstract void playerSetVolume(boolean muting, float leftVolume, float rightVolume);
+ abstract int playerSetAuxEffectSendLevel(boolean muting, float level);
}
diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java
index 9fafda48d6521..b429e22135bef 100644
--- a/media/java/android/media/SoundPool.java
+++ b/media/java/android/media/SoundPool.java
@@ -35,9 +35,6 @@ import android.os.ServiceManager;
import android.util.AndroidRuntimeException;
import android.util.Log;
-import com.android.internal.app.IAppOpsCallback;
-import com.android.internal.app.IAppOpsService;
-
/**
* The SoundPool class manages and plays audio resources for applications.
@@ -111,7 +108,7 @@ import com.android.internal.app.IAppOpsService;
* another level, a new SoundPool is created, sounds are loaded, and play
* resumes.
*/
-public class SoundPool {
+public class SoundPool extends PlayerBase {
static { System.loadLibrary("soundpool"); }
// SoundPool messages
@@ -130,10 +127,6 @@ public class SoundPool {
private final Object mLock;
private final AudioAttributes mAttributes;
- private final IAppOpsService mAppOps;
- private final IAppOpsCallback mAppOpsCallback;
-
- private static IAudioService sService;
/**
* Constructor. Constructs a SoundPool object with the following
@@ -156,32 +149,14 @@ public class SoundPool {
}
private SoundPool(int maxStreams, AudioAttributes attributes) {
+ super(attributes);
+
// do native setup
if (native_setup(new WeakReference(this), maxStreams, attributes) != 0) {
throw new RuntimeException("Native setup failed");
}
mLock = new Object();
mAttributes = attributes;
- IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
- mAppOps = IAppOpsService.Stub.asInterface(b);
- // initialize mHasAppOpsPlayAudio
- updateAppOpsPlayAudio();
- // register a callback to monitor whether the OP_PLAY_AUDIO is still allowed
- mAppOpsCallback = new IAppOpsCallback.Stub() {
- public void opChanged(int op, int uid, String packageName) {
- synchronized (mLock) {
- if (op == AppOpsManager.OP_PLAY_AUDIO) {
- updateAppOpsPlayAudio();
- }
- }
- }
- };
- try {
- mAppOps.startWatchingMode(AppOpsManager.OP_PLAY_AUDIO,
- ActivityThread.currentPackageName(), mAppOpsCallback);
- } catch (RemoteException e) {
- mHasAppOpsPlayAudio = false;
- }
}
/**
@@ -192,11 +167,7 @@ public class SoundPool {
* should be set to null.
*/
public final void release() {
- try {
- mAppOps.stopWatchingMode(mAppOpsCallback);
- } catch (RemoteException e) {
- // nothing to do here, the SoundPool is being released anyway
- }
+ baseRelease();
native_release();
}
@@ -333,9 +304,7 @@ public class SoundPool {
*/
public final int play(int soundID, float leftVolume, float rightVolume,
int priority, int loop, float rate) {
- if (isRestricted()) {
- leftVolume = rightVolume = 0;
- }
+ baseStart();
return _play(soundID, leftVolume, rightVolume, priority, loop, rate);
}
@@ -408,12 +377,26 @@ public class SoundPool {
* @param rightVolume right volume value (range = 0.0 to 1.0)
*/
public final void setVolume(int streamID, float leftVolume, float rightVolume) {
- if (isRestricted()) {
- return;
- }
+ // unlike other subclasses of PlayerBase, we are not calling
+ // baseSetVolume(leftVolume, rightVolume) as we need to keep track of each
+ // volume separately for each player, so we still send the command, but
+ // handle mute/unmute separately through playerSetVolume()
_setVolume(streamID, leftVolume, rightVolume);
}
+
+ @Override
+ void playerSetVolume(boolean muting, float leftVolume, float rightVolume) {
+ // not used here to control the player volume directly, but used to mute/unmute
+ _mute(muting);
+ }
+
+ @Override
+ int playerSetAuxEffectSendLevel(boolean muting, float level) {
+ // no aux send functionality so no-op
+ return AudioSystem.SUCCESS;
+ }
+
/**
* Similar, except set volume of all channels to same value.
* @hide
@@ -494,55 +477,6 @@ public class SoundPool {
}
}
- private static IAudioService getService()
- {
- if (sService != null) {
- return sService;
- }
- IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
- sService = IAudioService.Stub.asInterface(b);
- return sService;
- }
-
- private boolean isRestricted() {
- // check app ops
- if (mHasAppOpsPlayAudio) {
- return false;
- }
- // check bypass flag
- if ((mAttributes.getAllFlags() & AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY) != 0) {
- return false;
- }
- // check force audibility flag and camera restriction
- if ((mAttributes.getAllFlags() & AudioAttributes.FLAG_AUDIBILITY_ENFORCED) != 0) {
-// FIXME: should also check usage when set properly by camera app
-// && (mAttributes.getUsage() == AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
- boolean cameraSoundForced = false;
- try {
- cameraSoundForced = getService().isCameraSoundForced();
- } catch (RemoteException e) {
- Log.e(TAG, "Cannot access AudioService in isRestricted()");
- } catch (NullPointerException e) {
- Log.e(TAG, "Null AudioService in isRestricted()");
- }
- if (cameraSoundForced) {
- return false;
- }
- }
- return true;
- }
-
- private void updateAppOpsPlayAudio() {
- try {
- final int mode = mAppOps.checkAudioOperation(AppOpsManager.OP_PLAY_AUDIO,
- mAttributes.getUsage(),
- Process.myUid(), ActivityThread.currentPackageName());
- mHasAppOpsPlayAudio = (mode == AppOpsManager.MODE_ALLOWED);
- } catch (RemoteException e) {
- mHasAppOpsPlayAudio = false;
- }
- }
-
private native final int _load(FileDescriptor fd, long offset, long length, int priority);
private native final int native_setup(Object weakRef, int maxStreams,
@@ -553,6 +487,8 @@ public class SoundPool {
private native final void _setVolume(int streamID, float leftVolume, float rightVolume);
+ private native final void _mute(boolean muting);
+
// post event from native code to message handler
@SuppressWarnings("unchecked")
private static void postEventFromNative(Object ref, int msg, int arg1, int arg2, Object obj) {
diff --git a/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp
index d2dc44045c382..87092d0a5bd1e 100644
--- a/media/jni/soundpool/SoundPool.cpp
+++ b/media/jni/soundpool/SoundPool.cpp
@@ -60,6 +60,7 @@ SoundPool::SoundPool(int maxChannels, const audio_attributes_t* pAttributes)
ALOGW_IF(maxChannels != mMaxChannels, "App requested %d channels", maxChannels);
mQuit = false;
+ mMuted = false;
mDecodeThread = 0;
memcpy(&mAttributes, pAttributes, sizeof(audio_attributes_t));
mAllocated = 0;
@@ -366,6 +367,19 @@ void SoundPool::resume(int channelID)
}
}
+void SoundPool::mute(bool muting)
+{
+ ALOGV("mute(%d)", muting);
+ Mutex::Autolock lock(&mLock);
+ mMuted = muting;
+ if (!mChannels.empty()) {
+ for (List::iterator iter = mChannels.begin();
+ iter != mChannels.end(); ++iter) {
+ (*iter)->mute(muting);
+ }
+ }
+}
+
void SoundPool::autoResume()
{
ALOGV("autoResume()");
@@ -1032,7 +1046,7 @@ void SoundChannel::setVolume_l(float leftVolume, float rightVolume)
{
mLeftVolume = leftVolume;
mRightVolume = rightVolume;
- if (mAudioTrack != NULL)
+ if (mAudioTrack != NULL && !mMuted)
mAudioTrack->setVolume(leftVolume, rightVolume);
}
@@ -1042,6 +1056,19 @@ void SoundChannel::setVolume(float leftVolume, float rightVolume)
setVolume_l(leftVolume, rightVolume);
}
+void SoundChannel::mute(bool muting)
+{
+ Mutex::Autolock lock(&mLock);
+ mMuted = muting;
+ if (mAudioTrack != NULL) {
+ if (mMuted) {
+ mAudioTrack->setVolume(0.0f, 0.0f);
+ } else {
+ mAudioTrack->setVolume(mLeftVolume, mRightVolume);
+ }
+ }
+}
+
void SoundChannel::setLoop(int loop)
{
Mutex::Autolock lock(&mLock);
diff --git a/media/jni/soundpool/SoundPool.h b/media/jni/soundpool/SoundPool.h
index aff101f06748b..5c48a907c832b 100644
--- a/media/jni/soundpool/SoundPool.h
+++ b/media/jni/soundpool/SoundPool.h
@@ -114,13 +114,14 @@ class SoundChannel : public SoundEvent {
public:
enum state { IDLE, RESUMING, STOPPING, PAUSED, PLAYING };
SoundChannel() : mState(IDLE), mNumChannels(1),
- mPos(0), mToggle(0), mAutoPaused(false) {}
+ mPos(0), mToggle(0), mAutoPaused(false), mMuted(false) {}
~SoundChannel();
void init(SoundPool* soundPool);
void play(const sp& sample, int channelID, float leftVolume, float rightVolume,
int priority, int loop, float rate);
void setVolume_l(float leftVolume, float rightVolume);
void setVolume(float leftVolume, float rightVolume);
+ void mute(bool muting);
void stop_l();
void stop();
void pause();
@@ -154,6 +155,7 @@ private:
unsigned long mToggle;
bool mAutoPaused;
int mPrevSampleID;
+ bool mMuted;
};
// application object for managing a pool of sounds
@@ -168,6 +170,7 @@ public:
int play(int sampleID, float leftVolume, float rightVolume, int priority,
int loop, float rate);
void pause(int channelID);
+ void mute(bool muting);
void autoPause();
void resume(int channelID);
void autoResume();
@@ -222,6 +225,7 @@ private:
int mNextSampleID;
int mNextChannelID;
bool mQuit;
+ bool mMuted;
// callback
Mutex mCallbackLock;
diff --git a/media/jni/soundpool/android_media_SoundPool.cpp b/media/jni/soundpool/android_media_SoundPool.cpp
index ab3e3408bcacf..9d0c1f84fada5 100644
--- a/media/jni/soundpool/android_media_SoundPool.cpp
+++ b/media/jni/soundpool/android_media_SoundPool.cpp
@@ -131,6 +131,15 @@ android_media_SoundPool_setVolume(JNIEnv *env, jobject thiz, jint channelID,
ap->setVolume(channelID, (float) leftVolume, (float) rightVolume);
}
+static void
+android_media_SoundPool_mute(JNIEnv *env, jobject thiz, jboolean muting)
+{
+ ALOGV("android_media_SoundPool_mute(%d)", muting);
+ SoundPool *ap = MusterSoundPool(env, thiz);
+ if (ap == NULL) return;
+ ap->mute(muting == JNI_TRUE);
+}
+
static void
android_media_SoundPool_setPriority(JNIEnv *env, jobject thiz, jint channelID,
jint priority)
@@ -270,6 +279,10 @@ static JNINativeMethod gMethods[] = {
"(IFF)V",
(void *)android_media_SoundPool_setVolume
},
+ { "_mute",
+ "(Z)V",
+ (void *)android_media_SoundPool_mute
+ },
{ "setPriority",
"(II)V",
(void *)android_media_SoundPool_setPriority