diff options
author | John Spurlock <jspurlock@google.com> | 2014-03-10 08:33:35 -0400 |
---|---|---|
committer | John Spurlock <jspurlock@google.com> | 2014-03-19 15:32:51 -0400 |
commit | 1af30c7ac480e5d335f267a3ac3b2e6c748ce240 (patch) | |
tree | 584362f9abb3f28a21f144811fd86fc2bc3c73f0 /media | |
parent | 31dc8f701fb14e185bf1c1b35d68bd7d1a42a54a (diff) | |
download | frameworks_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.java | 8 | ||||
-rw-r--r-- | media/java/android/media/AudioTrack.java | 37 | ||||
-rw-r--r-- | media/java/android/media/MediaPlayer.java | 59 | ||||
-rw-r--r-- | media/java/android/media/SoundPool.java | 43 | ||||
-rw-r--r-- | media/jni/android_media_MediaPlayer.cpp | 21 | ||||
-rw-r--r-- | media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp | 4 |
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 }, |