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

Commit e0492bb5 authored by Jean-Michel Trivi's avatar Jean-Michel Trivi Committed by android-build-merger
Browse files

Merge "Player superclass for handling AppOps features" into nyc-dev

am: 4a182358

* commit '4a182358':
  Player superclass for handling AppOps features

Change-Id: I56e418afaa0a081a1df52c32608582e86d64c823
parents 9031fac2 4a182358
Loading
Loading
Loading
Loading
+20 −44
Original line number Diff line number Diff line
@@ -28,7 +28,6 @@ import java.util.Collection;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.app.ActivityThread;
import android.app.AppOpsManager;
import android.content.Context;
import android.os.Handler;
import android.os.IBinder;
@@ -41,7 +40,6 @@ import android.util.ArrayMap;
import android.util.Log;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IAppOpsService;

/**
 * The AudioTrack class manages and plays a single audio resource for Java applications.
@@ -78,7 +76,8 @@ import com.android.internal.app.IAppOpsService;
 *
 * AudioTrack is not final and thus permits subclasses, but such use is not recommended.
 */
public class AudioTrack implements AudioRouting
public class AudioTrack extends PlayerBase
                        implements AudioRouting
{
    //---------------------------------------------------------
    // Constants
@@ -271,7 +270,6 @@ public class AudioTrack implements AudioRouting
     */
    private int mStreamType = AudioManager.STREAM_MUSIC;

    private final AudioAttributes mAttributes;
    /**
     * The way audio is consumed by the audio sink, one of MODE_STATIC or MODE_STREAM.
     */
@@ -297,10 +295,6 @@ public class AudioTrack implements AudioRouting
     * Audio session ID
     */
    private int mSessionId = AudioManager.AUDIO_SESSION_ID_GENERATE;
    /**
     * Reference to the app-ops service.
     */
    private final IAppOpsService mAppOps;
    /**
     * HW_AV_SYNC track AV Sync Header
     */
@@ -448,11 +442,9 @@ public class AudioTrack implements AudioRouting
    public AudioTrack(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,
            int mode, int sessionId)
                    throws IllegalArgumentException {
        super(attributes);
        // mState already == STATE_UNINITIALIZED

        if (attributes == null) {
            throw new IllegalArgumentException("Illegal null AudioAttributes");
        }
        if (format == null) {
            throw new IllegalArgumentException("Illegal null AudioFormat");
        }
@@ -491,10 +483,6 @@ public class AudioTrack implements AudioRouting
        audioBuffSizeCheck(bufferSizeInBytes);

        mInitializationLooper = looper;
        IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
        mAppOps = IAppOpsService.Stub.asInterface(b);

        mAttributes = new AudioAttributes.Builder(attributes).build();

        if (sessionId < 0) {
            throw new IllegalArgumentException("Invalid audio session ID: "+sessionId);
@@ -534,9 +522,8 @@ public class AudioTrack implements AudioRouting
     * OpenSLES interface is realized.
     */
    /*package*/ AudioTrack(long nativeTrackInJavaObj) {
        super(new AudioAttributes.Builder().build());
        // "final"s
        mAttributes = null;
        mAppOps = null;
        mNativeTrackInJavaObj = 0;
        mJniData = 0;

@@ -961,12 +948,14 @@ public class AudioTrack implements AudioRouting
        } catch(IllegalStateException ise) {
            // don't raise an exception, we're releasing the resources.
        }
        baseRelease();
        native_release();
        mState = STATE_UNINITIALIZED;
    }

    @Override
    protected void finalize() {
        baseRelease();
        native_finalize();
    }

@@ -1492,19 +1481,20 @@ public class AudioTrack implements AudioRouting
     */
    @Deprecated
    public int setStereoVolume(float leftGain, float rightGain) {
        if (isRestricted()) {
            return SUCCESS;
        }
        if (mState == STATE_UNINITIALIZED) {
            return ERROR_INVALID_OPERATION;
        }

        leftGain = clampGainOrLevel(leftGain);
        rightGain = clampGainOrLevel(rightGain);
        baseSetVolume(leftGain, rightGain);
        return SUCCESS;
    }

        native_setVolume(leftGain, rightGain);
    @Override
    void playerSetVolume(float leftVolume, float rightVolume) {
        leftVolume = clampGainOrLevel(leftVolume);
        rightVolume = clampGainOrLevel(rightVolume);

        return SUCCESS;
        native_setVolume(leftVolume, rightVolume);
    }


@@ -1728,29 +1718,13 @@ public class AudioTrack implements AudioRouting
        if (mState != STATE_INITIALIZED) {
            throw new IllegalStateException("play() called on uninitialized AudioTrack.");
        }
        if (isRestricted()) {
            setVolume(0);
        }
        baseStart();
        synchronized(mPlayStateLock) {
            native_start();
            mPlayState = PLAYSTATE_PLAYING;
        }
    }

    private boolean isRestricted() {
        if ((mAttributes.getAllFlags() & AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY) != 0) {
            return false;
        }
        try {
            final int usage = AudioAttributes.usageForLegacyStreamType(mStreamType);
            final int mode = mAppOps.checkAudioOperation(AppOpsManager.OP_PLAY_AUDIO, usage,
                    Process.myUid(), ActivityThread.currentPackageName());
            return mode != AppOpsManager.MODE_ALLOWED;
        } catch (RemoteException e) {
            return false;
        }
    }

    /**
     * Stops playing the audio data.
     * When used on an instance created in {@link #MODE_STREAM} mode, audio will stop playing
@@ -2375,12 +2349,14 @@ public class AudioTrack implements AudioRouting
     *    {@link #ERROR_INVALID_OPERATION}, {@link #ERROR}
     */
    public int setAuxEffectSendLevel(float level) {
        if (isRestricted()) {
            return SUCCESS;
        }
        if (mState == STATE_UNINITIALIZED) {
            return ERROR_INVALID_OPERATION;
        }
        return baseSetAuxEffectSendLevel(level);
    }

    @Override
    int playerSetAuxEffectSendLevel(float level) {
        level = clampGainOrLevel(level);
        int err = native_setAuxEffectSendLevel(level);
        return err == 0 ? SUCCESS : ERROR;
+23 −34
Original line number Diff line number Diff line
@@ -20,7 +20,6 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityThread;
import android.app.AppOpsManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
@@ -34,8 +33,6 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.os.Process;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
import android.provider.Settings;
import android.system.ErrnoException;
@@ -56,7 +53,6 @@ import android.media.SubtitleData;
import android.media.SubtitleTrack.RenderingWidget;
import android.media.SyncParams;

import com.android.internal.app.IAppOpsService;
import com.android.internal.util.Preconditions;

import libcore.io.IoBridge;
@@ -66,7 +62,6 @@ import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.Runnable;
@@ -74,7 +69,6 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.net.InetSocketAddress;
import java.util.BitSet;
import java.util.HashSet;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;
@@ -561,7 +555,8 @@ import java.lang.ref.WeakReference;
 * thread by default has a Looper running).
 *
 */
public class MediaPlayer implements SubtitleController.Listener
public class MediaPlayer extends PlayerBase
                         implements SubtitleController.Listener
{
    /**
       Constant to retrieve only the new metadata since the last
@@ -615,7 +610,6 @@ public class MediaPlayer implements SubtitleController.Listener
    private PowerManager.WakeLock mWakeLock = null;
    private boolean mScreenOnWhilePlaying;
    private boolean mStayAwake;
    private final IAppOpsService mAppOps;
    private int mStreamType = AudioManager.USE_DEFAULT_STREAM_TYPE;
    private int mUsage = -1;
    private boolean mBypassInterruptionPolicy;
@@ -628,6 +622,7 @@ public class MediaPlayer implements SubtitleController.Listener
     * result in an exception.</p>
     */
    public MediaPlayer() {
        super(new AudioAttributes.Builder().build());

        Looper looper;
        if ((looper = Looper.myLooper()) != null) {
@@ -640,8 +635,6 @@ public class MediaPlayer implements SubtitleController.Listener

        mTimeProvider = new TimeProvider(this);
        mOpenSubtitleSources = new Vector<InputStream>();
        IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
        mAppOps = IAppOpsService.Stub.asInterface(b);

        /* Native setup requires a weak reference to our object.
         * It's easier to create it here than in C++.
@@ -1211,29 +1204,13 @@ public class MediaPlayer implements SubtitleController.Listener
     * @throws IllegalStateException if it is called in an invalid state
     */
    public void start() throws IllegalStateException {
        if (isRestricted()) {
            _setVolume(0, 0);
        }
        baseStart();
        stayAwake(true);
        _start();
    }

    private native void _start() throws IllegalStateException;

    private boolean isRestricted() {
        if (mBypassInterruptionPolicy) {
            return false;
        }
        try {
            final int usage = mUsage != -1 ? mUsage
                    : AudioAttributes.usageForLegacyStreamType(getAudioStreamType());
            final int mode = mAppOps.checkAudioOperation(AppOpsManager.OP_PLAY_AUDIO, usage,
                    Process.myUid(), ActivityThread.currentPackageName());
            return mode != AppOpsManager.MODE_ALLOWED;
        } catch (RemoteException e) {
            return false;
        }
    }

    private int getAudioStreamType() {
        if (mStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
@@ -1687,6 +1664,7 @@ public class MediaPlayer implements SubtitleController.Listener
     * at the same time.
     */
    public void release() {
        baseRelease();
        stayAwake(false);
        updateSurfaceScreenOn();
        mOnPreparedListener = null;
@@ -1756,6 +1734,8 @@ public class MediaPlayer implements SubtitleController.Listener
     * @see android.media.AudioManager
     */
    public void setAudioStreamType(int streamtype) {
        baseUpdateAudioAttributes(
                new AudioAttributes.Builder().setInternalLegacyStreamType(streamtype).build());
        _setAudioStreamType(streamtype);
        mStreamType = streamtype;
    }
@@ -1785,6 +1765,7 @@ public class MediaPlayer implements SubtitleController.Listener
            final String msg = "Cannot set AudioAttributes to null";
            throw new IllegalArgumentException(msg);
        }
        baseUpdateAudioAttributes(attributes);
        mUsage = attributes.getUsage();
        mBypassInterruptionPolicy = (attributes.getAllFlags()
                & AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY) != 0;
@@ -1826,9 +1807,11 @@ public class MediaPlayer implements SubtitleController.Listener
     * to be set independently.
     */
    public void setVolume(float leftVolume, float rightVolume) {
        if (isRestricted()) {
            return;
        baseSetVolume(leftVolume, rightVolume);
    }

    @Override
    void playerSetVolume(float leftVolume, float rightVolume) {
        _setVolume(leftVolume, rightVolume);
    }

@@ -1898,10 +1881,13 @@ public class MediaPlayer implements SubtitleController.Listener
     * @param level send level scalar
     */
    public void setAuxEffectSendLevel(float level) {
        if (isRestricted()) {
            return;
        baseSetAuxEffectSendLevel(level);
    }

    @Override
    int playerSetAuxEffectSendLevel(float level) {
        _setAuxEffectSendLevel(level);
        return AudioSystem.SUCCESS;
    }

    private native void _setAuxEffectSendLevel(float level);
@@ -2795,7 +2781,10 @@ public class MediaPlayer implements SubtitleController.Listener
    private native final int native_setRetransmitEndpoint(String addrString, int port);

    @Override
    protected void finalize() { native_finalize(); }
    protected void finalize() {
        baseRelease();
        native_finalize();
    }

    /* Do not change these values without updating their counterparts
     * in include/media/mediaplayer.h!
+193 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.media;

import java.lang.IllegalArgumentException;

import android.annotation.NonNull;
import android.app.ActivityThread;
import android.app.AppOpsManager;
import android.content.Context;
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;

import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsService;

/**
 * Class to encapsulate a number of common player operations:
 *   - AppOps for OP_PLAY_AUDIO
 *   - more to come (routing, transport control)
 * @hide
 */
public abstract class PlayerBase {

    // parameters of the player that affect AppOps
    protected AudioAttributes mAttributes;
    protected float mLeftVolume = 1.0f;
    protected float mRightVolume = 1.0f;
    protected float mAuxEffectSendLevel = 0.0f;

    // for AppOps
    private final IAppOpsService mAppOps;
    private final IAppOpsCallback mAppOpsCallback;
    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
     */
    PlayerBase(@NonNull AudioAttributes attr) {
        if (attr == null) {
            throw new IllegalArgumentException("Illegal null AudioAttributes");
        }
        mAttributes = attr;
        IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
        mAppOps = IAppOpsService.Stub.asInterface(b);
        // initialize mHasAppOpsPlayAudio
        updateAppOpsPlayAudio_sync();
        // 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 (mAppOpsLock) {
                    if (op == AppOpsManager.OP_PLAY_AUDIO) {
                        updateAppOpsPlayAudio_sync();
                    }
                }
            }
        };
        try {
            mAppOps.startWatchingMode(AppOpsManager.OP_PLAY_AUDIO,
                    ActivityThread.currentPackageName(), mAppOpsCallback);
        } catch (RemoteException e) {
            mHasAppOpsPlayAudio = false;
        }
    }


    /**
     * To be called whenever the audio attributes of the player change
     * @param attr non-null audio attributes
     */
    void baseUpdateAudioAttributes(@NonNull AudioAttributes attr) {
        if (attr == null) {
            throw new IllegalArgumentException("Illegal null AudioAttributes");
        }
        synchronized (mAppOpsLock) {
            mAttributes = attr;
            updateAppOpsPlayAudio_sync();
        }
    }

    void baseStart() {
        synchronized (mAppOpsLock) {
            if (isRestricted_sync()) {
                playerSetVolume(0, 0);
            }
        }
    }

    void baseSetVolume(float leftVolume, float rightVolume) {
        synchronized (mAppOpsLock) {
            mLeftVolume = leftVolume;
            mRightVolume = rightVolume;
            if (isRestricted_sync()) {
                return;
            }
        }
        playerSetVolume(leftVolume, rightVolume);
    }

    int baseSetAuxEffectSendLevel(float level) {
        synchronized (mAppOpsLock) {
            mAuxEffectSendLevel = level;
            if (isRestricted_sync()) {
                return AudioSystem.SUCCESS;
            }
        }
        return playerSetAuxEffectSendLevel(level);
    }

    /**
     * To be called from a subclass release or finalize method.
     * Releases AppOps related resources.
     */
    void baseRelease() {
        try {
            mAppOps.stopWatchingMode(mAppOpsCallback);
        } catch (RemoteException e) {
            // nothing to do here, the object is supposed to be released anyway
        }
    }

    /**
     * To be called whenever a condition that might affect audibility of this player is updated.
     * Must be called synchronized on mAppOpsLock.
     */
    void updateAppOpsPlayAudio_sync() {
        boolean oldHasAppOpsPlayAudio = mHasAppOpsPlayAudio;
        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;
        }

        // AppsOps alters a player's volume; when the restriction changes, reflect it on the actual
        // volume used by the player
        try {
            if (oldHasAppOpsPlayAudio != mHasAppOpsPlayAudio) {
                if (mHasAppOpsPlayAudio) {
                    playerSetVolume(mLeftVolume, mRightVolume);
                    playerSetAuxEffectSendLevel(mAuxEffectSendLevel);
                } else {
                    playerSetVolume(0.0f, 0.0f);
                    playerSetAuxEffectSendLevel(0.0f);
                }
            }
        } catch (Exception e) {
            // failing silently, player might not be in right state
        }
    }


    /**
     * 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
     * to call this method should be removed, and this method could become private.
     * FIXME can this method be private so subclasses don't have to worry about when to check
     *    the restrictions.
     * @return
     */
    boolean isRestricted_sync() {
        if ((mAttributes.getAllFlags() & AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY) != 0) {
            return false;
        }
        return !mHasAppOpsPlayAudio;
    }

    // Abstract methods a subclass needs to implement
    abstract void playerSetVolume(float leftVolume, float rightVolume);
    abstract int playerSetAuxEffectSendLevel(float level);
}