Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ac4a8b7f authored by Jean-Michel Trivi's avatar Jean-Michel Trivi Committed by Android (Google) Code Review
Browse files

Merge "Refactor SoundPool for appOps handling through PlayerBase"

parents 86829749 8e48c693
Loading
Loading
Loading
Loading
+5 −5
Original line number Diff line number Diff line
@@ -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;
    }
+4 −4
Original line number Diff line number Diff line
@@ -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;
    }

+52 −11
Original line number Diff line number Diff line
@@ -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);
}
+24 −88
Original line number Diff line number Diff line
@@ -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.</p>
 */
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<SoundPool>(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) {
+28 −1
Original line number Diff line number Diff line
@@ -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<SoundChannel*>::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);
Loading