summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorJohn Spurlock <jspurlock@google.com>2014-03-10 08:33:35 -0400
committerJohn Spurlock <jspurlock@google.com>2014-03-19 15:32:51 -0400
commit1af30c7ac480e5d335f267a3ac3b2e6c748ce240 (patch)
tree584362f9abb3f28a21f144811fd86fc2bc3c73f0 /media
parent31dc8f701fb14e185bf1c1b35d68bd7d1a42a54a (diff)
downloadframeworks_base-1af30c7ac480e5d335f267a3ac3b2e6c748ce240.zip
frameworks_base-1af30c7ac480e5d335f267a3ac3b2e6c748ce240.tar.gz
frameworks_base-1af30c7ac480e5d335f267a3ac3b2e6c748ce240.tar.bz2
Add stream-level suppression to vibrate/audio services.
- Add new audio restriction layer to app-ops. Restrictions add additional constraints to audio operations at a stream-level. Restrictions do not affect the persistable state, and are purely additive: that is, they can only impose additional contstraints, not enable something that has already been disabled. Restrictions also support a whitelisted set of exempt package names. - Add new audio stream-level checks to app-ops. - Implement a provisional OP_PLAY_AUDIO suppression to three java entry points MediaPlayer, AudioTrack, & SoundPool. - Enhance vibrator api to take stream information as an optional hint - the constants correspond to AudioManager stream types. OP_VIBRATE now supports the stream-level restriction check. - Simplify Vibrator subclasses by adding default implementations for two .vibrate calls. - Migrate NoMan's zen-mode control to use the new app-ops stream-level restriction mechanism. Change-Id: Ifae8952647202f728cf1c73e881452660c704678
Diffstat (limited to 'media')
-rw-r--r--media/java/android/media/AudioService.java8
-rw-r--r--media/java/android/media/AudioTrack.java37
-rw-r--r--media/java/android/media/MediaPlayer.java59
-rw-r--r--media/java/android/media/SoundPool.java43
-rw-r--r--media/jni/android_media_MediaPlayer.cpp21
-rw-r--r--media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp4
6 files changed, 157 insertions, 15 deletions
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 46b74da..fe510f6 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -258,7 +258,7 @@ public class AudioService extends IAudioService.Stub {
private final boolean mUseFixedVolume;
// stream names used by dumpStreamStates()
- private final String[] STREAM_NAMES = new String[] {
+ private static final String[] STREAM_NAMES = new String[] {
"STREAM_VOICE_CALL",
"STREAM_SYSTEM",
"STREAM_RING",
@@ -614,6 +614,12 @@ public class AudioService extends IAudioService.Stub {
pw.println(Integer.toHexString(mMuteAffectedStreams));
}
+ /** @hide */
+ public static String streamToString(int stream) {
+ if (stream >= 0 && stream < STREAM_NAMES.length) return STREAM_NAMES[stream];
+ if (stream == AudioManager.USE_DEFAULT_STREAM_TYPE) return "USE_DEFAULT_STREAM_TYPE";
+ return "UNKNOWN_STREAM_" + stream;
+ }
private void updateStreamVolumeAlias(boolean updateVolumes) {
int dtmfStreamAlias;
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 5611efb..dee8705 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -23,11 +23,20 @@ import java.nio.ByteBuffer;
import java.nio.NioUtils;
import android.annotation.IntDef;
+import android.app.ActivityThread;
+import android.app.AppOpsManager;
+import android.content.Context;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.util.Log;
+import com.android.internal.app.IAppOpsService;
+
/**
* The AudioTrack class manages and plays a single audio resource for Java applications.
@@ -239,7 +248,10 @@ public class AudioTrack
* Audio session ID
*/
private int mSessionId = AudioSystem.AUDIO_SESSION_ALLOCATE;
-
+ /**
+ * Reference to the app-ops service.
+ */
+ private final IAppOpsService mAppOps;
//--------------------------------
// Used exclusively by native code
@@ -343,6 +355,9 @@ public class AudioTrack
audioBuffSizeCheck(bufferSizeInBytes);
+ IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
+ mAppOps = IAppOpsService.Stub.asInterface(b);
+
if (sessionId < 0) {
throw new IllegalArgumentException("Invalid audio session ID: "+sessionId);
}
@@ -841,6 +856,9 @@ public class AudioTrack
* {@link #ERROR_INVALID_OPERATION}
*/
public int setStereoVolume(float leftVolume, float rightVolume) {
+ if (isRestricted()) {
+ return SUCCESS;
+ }
if (mState == STATE_UNINITIALIZED) {
return ERROR_INVALID_OPERATION;
}
@@ -1014,13 +1032,25 @@ public class AudioTrack
if (mState != STATE_INITIALIZED) {
throw new IllegalStateException("play() called on uninitialized AudioTrack.");
}
-
+ if (isRestricted()) {
+ setVolume(0);
+ }
synchronized(mPlayStateLock) {
native_start();
mPlayState = PLAYSTATE_PLAYING;
}
}
+ private boolean isRestricted() {
+ try {
+ final int mode = mAppOps.checkAudioOperation(AppOpsManager.OP_PLAY_AUDIO, mStreamType,
+ 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
@@ -1296,6 +1326,9 @@ public class AudioTrack
* {@link #ERROR_INVALID_OPERATION}
*/
public int setAuxEffectSendLevel(float level) {
+ if (isRestricted()) {
+ return SUCCESS;
+ }
if (mState == STATE_UNINITIALIZED) {
return ERROR_INVALID_OPERATION;
}
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index e20a4af..1b92410 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -16,6 +16,8 @@
package android.media;
+import android.app.ActivityThread;
+import android.app.AppOpsManager;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@@ -32,6 +34,8 @@ 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.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
@@ -42,6 +46,8 @@ import android.media.MediaTimeProvider;
import android.media.SubtitleController;
import android.media.SubtitleData;
+import com.android.internal.app.IAppOpsService;
+
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
@@ -576,6 +582,8 @@ 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;
/**
* Default constructor. Consider using one of the create() methods for
@@ -599,6 +607,8 @@ public class MediaPlayer implements SubtitleController.Listener
mOutOfBandSubtitleTracks = new Vector<SubtitleTrack>();
mOpenSubtitleSources = new Vector<InputStream>();
mInbandSubtitleTracks = new SubtitleTrack[0];
+ 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++.
@@ -1055,13 +1065,35 @@ public class MediaPlayer implements SubtitleController.Listener
*
* @throws IllegalStateException if it is called in an invalid state
*/
- public void start() throws IllegalStateException {
+ public void start() throws IllegalStateException {
+ if (isRestricted()) {
+ _setVolume(0, 0);
+ }
stayAwake(true);
_start();
}
private native void _start() throws IllegalStateException;
+ private boolean isRestricted() {
+ try {
+ final int mode = mAppOps.checkAudioOperation(AppOpsManager.OP_PLAY_AUDIO,
+ getAudioStreamType(), Process.myUid(), ActivityThread.currentPackageName());
+ return mode != AppOpsManager.MODE_ALLOWED;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ private int getAudioStreamType() {
+ if (mStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
+ mStreamType = _getAudioStreamType();
+ }
+ return mStreamType;
+ }
+
+ private native int _getAudioStreamType() throws IllegalStateException;
+
/**
* Stops playback after playback has been stopped or paused.
*
@@ -1402,7 +1434,12 @@ public class MediaPlayer implements SubtitleController.Listener
* @param streamtype the audio stream type
* @see android.media.AudioManager
*/
- public native void setAudioStreamType(int streamtype);
+ public void setAudioStreamType(int streamtype) {
+ _setAudioStreamType(streamtype);
+ mStreamType = streamtype;
+ }
+
+ private native void _setAudioStreamType(int streamtype);
/**
* Sets the player to be looping or non-looping.
@@ -1435,7 +1472,14 @@ public class MediaPlayer implements SubtitleController.Listener
* The single parameter form below is preferred if the channel volumes don't need
* to be set independently.
*/
- public native void setVolume(float leftVolume, float rightVolume);
+ public void setVolume(float leftVolume, float rightVolume) {
+ if (isRestricted()) {
+ return;
+ }
+ _setVolume(leftVolume, rightVolume);
+ }
+
+ private native void _setVolume(float leftVolume, float rightVolume);
/**
* Similar, excepts sets volume of all channels to same value.
@@ -1500,7 +1544,14 @@ public class MediaPlayer implements SubtitleController.Listener
* 0 < x <= R -> level = 10^(72*(x-R)/20/R)
* @param level send level scalar
*/
- public native void setAuxEffectSendLevel(float level);
+ public void setAuxEffectSendLevel(float level) {
+ if (isRestricted()) {
+ return;
+ }
+ _setAuxEffectSendLevel(level);
+ }
+
+ private native void _setAuxEffectSendLevel(float level);
/*
* @param request Parcel destinated to the media player. The
diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java
index f1b256e..14f0c69 100644
--- a/media/java/android/media/SoundPool.java
+++ b/media/java/android/media/SoundPool.java
@@ -20,16 +20,24 @@ import java.io.File;
import java.io.FileDescriptor;
import java.lang.ref.WeakReference;
+import android.app.ActivityThread;
+import android.app.AppOpsManager;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.SystemProperties;
import android.util.AndroidRuntimeException;
import android.util.Log;
+import com.android.internal.app.IAppOpsService;
+
/**
* The SoundPool class manages and plays audio resources for applications.
@@ -449,6 +457,8 @@ public class SoundPool {
private SoundPool mProxy;
private final Object mLock;
+ private final int mStreamType;
+ private final IAppOpsService mAppOps;
// SoundPool messages
//
@@ -463,6 +473,9 @@ public class SoundPool {
}
mLock = new Object();
mProxy = proxy;
+ mStreamType = streamType;
+ IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
+ mAppOps = IAppOpsService.Stub.asInterface(b);
}
public int load(String path, int priority)
@@ -522,9 +535,27 @@ public class SoundPool {
public native final boolean unload(int soundID);
- public native final int play(int soundID, float leftVolume, float rightVolume,
+ public final int play(int soundID, float leftVolume, float rightVolume,
+ int priority, int loop, float rate) {
+ if (isRestricted()) {
+ leftVolume = rightVolume = 0;
+ }
+ return _play(soundID, leftVolume, rightVolume, priority, loop, rate);
+ }
+
+ public native final int _play(int soundID, float leftVolume, float rightVolume,
int priority, int loop, float rate);
+ private boolean isRestricted() {
+ try {
+ final int mode = mAppOps.checkAudioOperation(AppOpsManager.OP_PLAY_AUDIO,
+ mStreamType, Process.myUid(), ActivityThread.currentPackageName());
+ return mode != AppOpsManager.MODE_ALLOWED;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
public native final void pause(int streamID);
public native final void resume(int streamID);
@@ -535,8 +566,14 @@ public class SoundPool {
public native final void stop(int streamID);
- public native final void setVolume(int streamID,
- float leftVolume, float rightVolume);
+ public final void setVolume(int streamID, float leftVolume, float rightVolume) {
+ if (isRestricted()) {
+ return;
+ }
+ _setVolume(streamID, leftVolume, rightVolume);
+ }
+
+ private native final void _setVolume(int streamID, float leftVolume, float rightVolume);
public void setVolume(int streamID, float volume) {
setVolume(streamID, volume, volume);
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index dc3ae5b..abebd48 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -500,6 +500,20 @@ android_media_MediaPlayer_setAudioStreamType(JNIEnv *env, jobject thiz, jint str
process_media_player_call( env, thiz, mp->setAudioStreamType((audio_stream_type_t) streamtype) , NULL, NULL );
}
+static jint
+android_media_MediaPlayer_getAudioStreamType(JNIEnv *env, jobject thiz)
+{
+ sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
+ if (mp == NULL ) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return 0;
+ }
+ audio_stream_type_t streamtype;
+ process_media_player_call( env, thiz, mp->getAudioStreamType(&streamtype), NULL, NULL );
+ ALOGV("getAudioStreamType: %d (streamtype)", streamtype);
+ return (jint) streamtype;
+}
+
static void
android_media_MediaPlayer_setLooping(JNIEnv *env, jobject thiz, jboolean looping)
{
@@ -841,10 +855,11 @@ static JNINativeMethod gMethods[] = {
{"getDuration", "()I", (void *)android_media_MediaPlayer_getDuration},
{"_release", "()V", (void *)android_media_MediaPlayer_release},
{"_reset", "()V", (void *)android_media_MediaPlayer_reset},
- {"setAudioStreamType", "(I)V", (void *)android_media_MediaPlayer_setAudioStreamType},
+ {"_setAudioStreamType", "(I)V", (void *)android_media_MediaPlayer_setAudioStreamType},
+ {"_getAudioStreamType", "()I", (void *)android_media_MediaPlayer_getAudioStreamType},
{"setLooping", "(Z)V", (void *)android_media_MediaPlayer_setLooping},
{"isLooping", "()Z", (void *)android_media_MediaPlayer_isLooping},
- {"setVolume", "(FF)V", (void *)android_media_MediaPlayer_setVolume},
+ {"_setVolume", "(FF)V", (void *)android_media_MediaPlayer_setVolume},
{"native_invoke", "(Landroid/os/Parcel;Landroid/os/Parcel;)I",(void *)android_media_MediaPlayer_invoke},
{"native_setMetadataFilter", "(Landroid/os/Parcel;)I", (void *)android_media_MediaPlayer_setMetadataFilter},
{"native_getMetadata", "(ZZLandroid/os/Parcel;)Z", (void *)android_media_MediaPlayer_getMetadata},
@@ -853,7 +868,7 @@ static JNINativeMethod gMethods[] = {
{"native_finalize", "()V", (void *)android_media_MediaPlayer_native_finalize},
{"getAudioSessionId", "()I", (void *)android_media_MediaPlayer_get_audio_session_id},
{"setAudioSessionId", "(I)V", (void *)android_media_MediaPlayer_set_audio_session_id},
- {"setAuxEffectSendLevel", "(F)V", (void *)android_media_MediaPlayer_setAuxEffectSendLevel},
+ {"_setAuxEffectSendLevel", "(F)V", (void *)android_media_MediaPlayer_setAuxEffectSendLevel},
{"attachAuxEffect", "(I)V", (void *)android_media_MediaPlayer_attachAuxEffect},
{"native_pullBatteryData", "(Landroid/os/Parcel;)I", (void *)android_media_MediaPlayer_pullBatteryData},
{"native_setRetransmitEndpoint", "(Ljava/lang/String;I)I", (void *)android_media_MediaPlayer_setRetransmitEndpoint},
diff --git a/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp b/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp
index 9cc55ab..bda3b6b 100644
--- a/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp
+++ b/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp
@@ -229,7 +229,7 @@ static JNINativeMethod gMethods[] = {
"(I)Z",
(void *)android_media_SoundPool_SoundPoolImpl_unload
},
- { "play",
+ { "_play",
"(IFFIIF)I",
(void *)android_media_SoundPool_SoundPoolImpl_play
},
@@ -253,7 +253,7 @@ static JNINativeMethod gMethods[] = {
"(I)V",
(void *)android_media_SoundPool_SoundPoolImpl_stop
},
- { "setVolume",
+ { "_setVolume",
"(IFF)V",
(void *)android_media_SoundPool_SoundPoolImpl_setVolume
},