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