diff options
18 files changed, 437 insertions, 174 deletions
diff --git a/api/current.txt b/api/current.txt index e72354b..139e927 100644 --- a/api/current.txt +++ b/api/current.txt @@ -19546,8 +19546,10 @@ package android.os { public abstract class Vibrator { method public abstract void cancel(); method public abstract boolean hasVibrator(); - method public abstract void vibrate(long); - method public abstract void vibrate(long[], int); + method public void vibrate(long); + method public void vibrate(long, int); + method public void vibrate(long[], int); + method public void vibrate(long[], int, int); } public class WorkSource implements android.os.Parcelable { diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 079cf7a..b616c1e 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -780,6 +780,25 @@ public class AppOpsManager { } } + /** + * Set a non-persisted restriction on an audio operation at a stream-level. + * Restrictions are temporary additional constraints imposed on top of the persisted rules + * defined by {@link #setMode}. + * + * @param code The operation to restrict. + * @param stream The {@link android.media.AudioManager} stream type. + * @param mode The restriction mode (MODE_IGNORED,MODE_ERRORED) or MODE_ALLOWED to unrestrict. + * @param exceptionPackages Optional list of packages to exclude from the restriction. + * @hide + */ + public void setRestriction(int code, int stream, int mode, String[] exceptionPackages) { + try { + final int uid = Binder.getCallingUid(); + mService.setAudioRestriction(code, stream, uid, mode, exceptionPackages); + } catch (RemoteException e) { + } + } + /** @hide */ public void resetAllModes() { try { @@ -1009,6 +1028,35 @@ public class AppOpsManager { } /** + * Like {@link #checkOp} but at a stream-level for audio operations. + * @hide + */ + public int checkAudioOp(int op, int stream, int uid, String packageName) { + try { + final int mode = mService.checkAudioOperation(op, stream, uid, packageName); + if (mode == MODE_ERRORED) { + throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName)); + } + return mode; + } catch (RemoteException e) { + } + return MODE_IGNORED; + } + + /** + * Like {@link #checkAudioOp} but instead of throwing a {@link SecurityException} it + * returns {@link #MODE_ERRORED}. + * @hide + */ + public int checkAudioOpNoThrow(int op, int stream, int uid, String packageName) { + try { + return mService.checkAudioOperation(op, stream, uid, packageName); + } catch (RemoteException e) { + } + return MODE_IGNORED; + } + + /** * Make note of an application performing an operation. Note that you must pass * in both the uid and name of the application to be checked; this function will verify * that these two match, and if not, return {@link #MODE_IGNORED}. If this call diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java index e3a3830..0c0dfe9 100644 --- a/core/java/android/hardware/input/InputManager.java +++ b/core/java/android/hardware/input/InputManager.java @@ -853,13 +853,21 @@ public final class InputManager { return true; } + /** + * @hide + */ @Override - public void vibrate(long milliseconds) { + public void vibrate(int owningUid, String owningPackage, long milliseconds, + int streamHint) { vibrate(new long[] { 0, milliseconds}, -1); } + /** + * @hide + */ @Override - public void vibrate(long[] pattern, int repeat) { + public void vibrate(int owningUid, String owningPackage, long[] pattern, int repeat, + int streamHint) { if (repeat >= pattern.length) { throw new ArrayIndexOutOfBoundsException(); } @@ -870,22 +878,6 @@ public final class InputManager { } } - /** - * @hide - */ - @Override - public void vibrate(int owningUid, String owningPackage, long milliseconds) { - vibrate(milliseconds); - } - - /** - * @hide - */ - @Override - public void vibrate(int owningUid, String owningPackage, long[] pattern, int repeat) { - vibrate(pattern, repeat); - } - @Override public void cancel() { try { diff --git a/core/java/android/os/IVibratorService.aidl b/core/java/android/os/IVibratorService.aidl index 456ffb1..4854bc0 100644 --- a/core/java/android/os/IVibratorService.aidl +++ b/core/java/android/os/IVibratorService.aidl @@ -20,8 +20,8 @@ package android.os; interface IVibratorService { boolean hasVibrator(); - void vibrate(int uid, String packageName, long milliseconds, IBinder token); - void vibratePattern(int uid, String packageName, in long[] pattern, int repeat, IBinder token); + void vibrate(int uid, String packageName, long milliseconds, int streamHint, IBinder token); + void vibratePattern(int uid, String packageName, in long[] pattern, int repeat, int streamHint, IBinder token); void cancelVibrate(IBinder token); } diff --git a/core/java/android/os/NullVibrator.java b/core/java/android/os/NullVibrator.java index af90bdb..536da32 100644 --- a/core/java/android/os/NullVibrator.java +++ b/core/java/android/os/NullVibrator.java @@ -36,22 +36,11 @@ public class NullVibrator extends Vibrator { return false; } - @Override - public void vibrate(long milliseconds) { - } - - @Override - public void vibrate(long[] pattern, int repeat) { - if (repeat >= pattern.length) { - throw new ArrayIndexOutOfBoundsException(); - } - } - /** * @hide */ @Override - public void vibrate(int owningUid, String owningPackage, long milliseconds) { + public void vibrate(int owningUid, String owningPackage, long milliseconds, int streamHint) { vibrate(milliseconds); } @@ -59,8 +48,11 @@ public class NullVibrator extends Vibrator { * @hide */ @Override - public void vibrate(int owningUid, String owningPackage, long[] pattern, int repeat) { - vibrate(pattern, repeat); + public void vibrate(int owningUid, String owningPackage, long[] pattern, int repeat, + int streamHint) { + if (repeat >= pattern.length) { + throw new ArrayIndexOutOfBoundsException(); + } } @Override diff --git a/core/java/android/os/SystemVibrator.java b/core/java/android/os/SystemVibrator.java index 700f80d..13bc4f6 100644 --- a/core/java/android/os/SystemVibrator.java +++ b/core/java/android/os/SystemVibrator.java @@ -16,7 +16,6 @@ package android.os; -import android.app.ActivityThread; import android.content.Context; import android.util.Log; @@ -28,18 +27,16 @@ import android.util.Log; public class SystemVibrator extends Vibrator { private static final String TAG = "Vibrator"; - private final String mPackageName; private final IVibratorService mService; private final Binder mToken = new Binder(); public SystemVibrator() { - mPackageName = ActivityThread.currentPackageName(); mService = IVibratorService.Stub.asInterface( ServiceManager.getService("vibrator")); } public SystemVibrator(Context context) { - mPackageName = context.getOpPackageName(); + super(context); mService = IVibratorService.Stub.asInterface( ServiceManager.getService("vibrator")); } @@ -57,27 +54,17 @@ public class SystemVibrator extends Vibrator { return false; } - @Override - public void vibrate(long milliseconds) { - vibrate(Process.myUid(), mPackageName, milliseconds); - } - - @Override - public void vibrate(long[] pattern, int repeat) { - vibrate(Process.myUid(), mPackageName, pattern, repeat); - } - /** * @hide */ @Override - public void vibrate(int owningUid, String owningPackage, long milliseconds) { + public void vibrate(int owningUid, String owningPackage, long milliseconds, int streamHint) { if (mService == null) { Log.w(TAG, "Failed to vibrate; no vibrator service."); return; } try { - mService.vibrate(owningUid, owningPackage, milliseconds, mToken); + mService.vibrate(owningUid, owningPackage, milliseconds, streamHint, mToken); } catch (RemoteException e) { Log.w(TAG, "Failed to vibrate.", e); } @@ -87,7 +74,8 @@ public class SystemVibrator extends Vibrator { * @hide */ @Override - public void vibrate(int owningUid, String owningPackage, long[] pattern, int repeat) { + public void vibrate(int owningUid, String owningPackage, long[] pattern, int repeat, + int streamHint) { if (mService == null) { Log.w(TAG, "Failed to vibrate; no vibrator service."); return; @@ -97,7 +85,8 @@ public class SystemVibrator extends Vibrator { // anyway if (repeat < pattern.length) { try { - mService.vibratePattern(owningUid, owningPackage, pattern, repeat, mToken); + mService.vibratePattern(owningUid, owningPackage, pattern, repeat, streamHint, + mToken); } catch (RemoteException e) { Log.w(TAG, "Failed to vibrate.", e); } diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java index 5d55143..53034d8 100644 --- a/core/java/android/os/Vibrator.java +++ b/core/java/android/os/Vibrator.java @@ -16,7 +16,9 @@ package android.os; +import android.app.ActivityThread; import android.content.Context; +import android.media.AudioManager; /** * Class that operates the vibrator on the device. @@ -28,10 +30,21 @@ import android.content.Context; * {@link Context#getSystemService} with {@link Context#VIBRATOR_SERVICE} as the argument. */ public abstract class Vibrator { + + private final String mPackageName; + /** * @hide to prevent subclassing from outside of the framework */ public Vibrator() { + mPackageName = ActivityThread.currentPackageName(); + } + + /** + * @hide to prevent subclassing from outside of the framework + */ + protected Vibrator(Context context) { + mPackageName = context.getOpPackageName(); } /** @@ -40,7 +53,7 @@ public abstract class Vibrator { * @return True if the hardware has a vibrator, else false. */ public abstract boolean hasVibrator(); - + /** * Vibrate constantly for the specified period of time. * <p>This method requires the caller to hold the permission @@ -48,7 +61,23 @@ public abstract class Vibrator { * * @param milliseconds The number of milliseconds to vibrate. */ - public abstract void vibrate(long milliseconds); + public void vibrate(long milliseconds) { + vibrate(milliseconds, AudioManager.USE_DEFAULT_STREAM_TYPE); + } + + /** + * Vibrate constantly for the specified period of time. + * <p>This method requires the caller to hold the permission + * {@link android.Manifest.permission#VIBRATE}. + * + * @param milliseconds The number of milliseconds to vibrate. + * @param streamHint An {@link AudioManager} stream type corresponding to the vibration type. + * For example, specify {@link AudioManager.STREAM_ALARM} for alarm vibrations or + * {@link AudioManager.STREAM_RING} for vibrations associated with incoming calls. + */ + public void vibrate(long milliseconds, int streamHint) { + vibrate(Process.myUid(), mPackageName, milliseconds, streamHint); + } /** * Vibrate with a given pattern. @@ -70,21 +99,52 @@ public abstract class Vibrator { * @param repeat the index into pattern at which to repeat, or -1 if * you don't want to repeat. */ - public abstract void vibrate(long[] pattern, int repeat); + public void vibrate(long[] pattern, int repeat) { + vibrate(pattern, repeat, AudioManager.USE_DEFAULT_STREAM_TYPE); + } + + /** + * Vibrate with a given pattern. + * + * <p> + * Pass in an array of ints that are the durations for which to turn on or off + * the vibrator in milliseconds. The first value indicates the number of milliseconds + * to wait before turning the vibrator on. The next value indicates the number of milliseconds + * for which to keep the vibrator on before turning it off. Subsequent values alternate + * between durations in milliseconds to turn the vibrator off or to turn the vibrator on. + * </p><p> + * To cause the pattern to repeat, pass the index into the pattern array at which + * to start the repeat, or -1 to disable repeating. + * </p> + * <p>This method requires the caller to hold the permission + * {@link android.Manifest.permission#VIBRATE}. + * + * @param pattern an array of longs of times for which to turn the vibrator on or off. + * @param repeat the index into pattern at which to repeat, or -1 if + * you don't want to repeat. + * @param streamHint An {@link AudioManager} stream type corresponding to the vibration type. + * For example, specify {@link AudioManager.STREAM_ALARM} for alarm vibrations or + * {@link AudioManager.STREAM_RING} for vibrations associated with incoming calls. + */ + public void vibrate(long[] pattern, int repeat, int streamHint) { + vibrate(Process.myUid(), mPackageName, pattern, repeat, streamHint); + } /** * @hide - * Like {@link #vibrate(long)}, but allowing the caller to specify that + * Like {@link #vibrate(long, int)}, but allowing the caller to specify that * the vibration is owned by someone else. */ - public abstract void vibrate(int owningUid, String owningPackage, long milliseconds); + public abstract void vibrate(int owningUid, String owningPackage, + long milliseconds, int streamHint); /** * @hide - * Like {@link #vibrate(long[], int)}, but allowing the caller to specify that + * Like {@link #vibrate(long[], int, int)}, but allowing the caller to specify that * the vibration is owned by someone else. */ - public abstract void vibrate(int owningUid, String owningPackage, long[] pattern, int repeat); + public abstract void vibrate(int owningUid, String owningPackage, + long[] pattern, int repeat, int streamHint); /** * Turn the vibrator off. diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl index 16c41f3..cd75010 100644 --- a/core/java/com/android/internal/app/IAppOpsService.aidl +++ b/core/java/com/android/internal/app/IAppOpsService.aidl @@ -36,4 +36,6 @@ interface IAppOpsService { List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName, in int[] ops); void setMode(int code, int uid, String packageName, int mode); void resetAllModes(); + int checkAudioOperation(int code, int stream, int uid, String packageName); + void setAudioRestriction(int code, int stream, int uid, int mode, in String[] exceptionPackages); } 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 }, diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index 1cca164..afa68da 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -5083,10 +5083,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { } if (pattern.length == 1) { // One-shot vibration - mVibrator.vibrate(owningUid, owningPackage, pattern[0]); + mVibrator.vibrate(owningUid, owningPackage, pattern[0], AudioManager.STREAM_SYSTEM); } else { // Pattern vibration - mVibrator.vibrate(owningUid, owningPackage, pattern, -1); + mVibrator.vibrate(owningUid, owningPackage, pattern, -1, AudioManager.STREAM_SYSTEM); } return true; } diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java index e5615c0..e26747c 100644 --- a/services/core/java/com/android/server/AppOpsService.java +++ b/services/core/java/com/android/server/AppOpsService.java @@ -33,6 +33,7 @@ import android.app.AppOpsManager; import android.content.Context; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; +import android.media.AudioService; import android.os.AsyncTask; import android.os.Binder; import android.os.Handler; @@ -42,6 +43,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.AtomicFile; import android.util.Log; import android.util.Pair; @@ -123,6 +125,8 @@ public class AppOpsService extends IAppOpsService.Stub { = new ArrayMap<String, ArrayList<Callback>>(); final ArrayMap<IBinder, Callback> mModeWatchers = new ArrayMap<IBinder, Callback>(); + final SparseArray<SparseArray<Restriction>> mAudioRestrictions + = new SparseArray<SparseArray<Restriction>>(); public final class Callback implements DeathRecipient { final IAppOpsCallback mCallback; @@ -553,6 +557,58 @@ public class AppOpsService extends IAppOpsService.Stub { } @Override + public int checkAudioOperation(int code, int stream, int uid, String packageName) { + synchronized (this) { + final int mode = checkRestrictionLocked(code, stream, uid, packageName); + if (mode != AppOpsManager.MODE_ALLOWED) { + return mode; + } + } + return checkOperation(code, uid, packageName); + } + + private int checkRestrictionLocked(int code, int stream, int uid, String packageName) { + final SparseArray<Restriction> streamRestrictions = mAudioRestrictions.get(code); + if (streamRestrictions != null) { + final Restriction r = streamRestrictions.get(stream); + if (r != null && !r.exceptionPackages.contains(packageName)) { + return r.mode; + } + } + return AppOpsManager.MODE_ALLOWED; + } + + @Override + public void setAudioRestriction(int code, int stream, int uid, int mode, + String[] exceptionPackages) { + verifyIncomingUid(uid); + verifyIncomingOp(code); + synchronized (this) { + SparseArray<Restriction> streamRestrictions = mAudioRestrictions.get(code); + if (streamRestrictions == null) { + streamRestrictions = new SparseArray<Restriction>(); + mAudioRestrictions.put(code, streamRestrictions); + } + streamRestrictions.remove(stream); + if (mode != AppOpsManager.MODE_ALLOWED) { + final Restriction r = new Restriction(); + r.mode = mode; + if (exceptionPackages != null) { + final int N = exceptionPackages.length; + r.exceptionPackages = new ArraySet<String>(N); + for (int i = 0; i < N; i++) { + final String pkg = exceptionPackages[i]; + if (pkg != null) { + r.exceptionPackages.add(pkg.trim()); + } + } + } + streamRestrictions.put(stream, r); + } + } + } + + @Override public int checkPackage(int uid, String packageName) { synchronized (this) { if (getOpsLocked(uid, packageName, true) != null) { @@ -1048,6 +1104,31 @@ public class AppOpsService extends IAppOpsService.Stub { } } } + if (mAudioRestrictions.size() > 0) { + boolean printedHeader = false; + for (int o=0; o<mAudioRestrictions.size(); o++) { + final String op = AppOpsManager.opToName(mAudioRestrictions.keyAt(o)); + final SparseArray<Restriction> restrictions = mAudioRestrictions.valueAt(o); + for (int i=0; i<restrictions.size(); i++) { + if (!printedHeader){ + pw.println(" Audio Restrictions:"); + printedHeader = true; + needSep = true; + } + final int stream = restrictions.keyAt(i); + pw.print(" "); pw.print(op); + pw.print(" stream="); pw.print(AudioService.streamToString(stream)); + Restriction r = restrictions.valueAt(i); + pw.print(": mode="); pw.println(r.mode); + if (!r.exceptionPackages.isEmpty()) { + pw.println(" Exceptions:"); + for (int j=0; j<r.exceptionPackages.size(); j++) { + pw.print(" "); pw.println(r.exceptionPackages.valueAt(j)); + } + } + } + } + } if (needSep) { pw.println(); } @@ -1080,4 +1161,10 @@ public class AppOpsService extends IAppOpsService.Stub { } } } + + private static final class Restriction { + private static final ArraySet<String> NO_EXCEPTIONS = new ArraySet<String>(); + int mode; + ArraySet<String> exceptionPackages = NO_EXCEPTIONS; + } } diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java index 28eb948..52f9aa9 100644 --- a/services/core/java/com/android/server/VibratorService.java +++ b/services/core/java/com/android/server/VibratorService.java @@ -84,24 +84,27 @@ public class VibratorService extends IVibratorService.Stub private final long mStartTime; private final long[] mPattern; private final int mRepeat; + private final int mStreamHint; private final int mUid; private final String mPackageName; - Vibration(IBinder token, long millis, int uid, String packageName) { - this(token, millis, null, 0, uid, packageName); + Vibration(IBinder token, long millis, int streamHint, int uid, String packageName) { + this(token, millis, null, 0, streamHint, uid, packageName); } - Vibration(IBinder token, long[] pattern, int repeat, int uid, String packageName) { - this(token, 0, pattern, repeat, uid, packageName); + Vibration(IBinder token, long[] pattern, int repeat, int streamHint, int uid, + String packageName) { + this(token, 0, pattern, repeat, streamHint, uid, packageName); } private Vibration(IBinder token, long millis, long[] pattern, - int repeat, int uid, String packageName) { + int repeat, int streamHint, int uid, String packageName) { mToken = token; mTimeout = millis; mStartTime = SystemClock.uptimeMillis(); mPattern = pattern; mRepeat = repeat; + mStreamHint = streamHint; mUid = uid; mPackageName = packageName; } @@ -191,7 +194,8 @@ public class VibratorService extends IVibratorService.Stub Binder.getCallingPid(), Binder.getCallingUid(), null); } - public void vibrate(int uid, String packageName, long milliseconds, IBinder token) { + public void vibrate(int uid, String packageName, long milliseconds, int streamHint, + IBinder token) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Requires VIBRATE permission"); @@ -207,7 +211,7 @@ public class VibratorService extends IVibratorService.Stub return; } - Vibration vib = new Vibration(token, milliseconds, uid, packageName); + Vibration vib = new Vibration(token, milliseconds, streamHint, uid, packageName); final long ident = Binder.clearCallingIdentity(); try { @@ -233,7 +237,7 @@ public class VibratorService extends IVibratorService.Stub } public void vibratePattern(int uid, String packageName, long[] pattern, int repeat, - IBinder token) { + int streamHint, IBinder token) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Requires VIBRATE permission"); @@ -258,7 +262,7 @@ public class VibratorService extends IVibratorService.Stub return; } - Vibration vib = new Vibration(token, pattern, repeat, uid, packageName); + Vibration vib = new Vibration(token, pattern, repeat, streamHint, uid, packageName); try { token.linkToDeath(vib, 0); } catch (RemoteException e) { @@ -342,8 +346,12 @@ public class VibratorService extends IVibratorService.Stub // Lock held on mVibrations private void startVibrationLocked(final Vibration vib) { try { - int mode = mAppOpsService.startOperation(AppOpsManager.getToken(mAppOpsService), + int mode = mAppOpsService.checkAudioOperation(AppOpsManager.OP_VIBRATE, + vib.mStreamHint, vib.mUid, vib.mPackageName); + if (mode == AppOpsManager.MODE_ALLOWED) { + mode = mAppOpsService.startOperation(AppOpsManager.getToken(mAppOpsService), AppOpsManager.OP_VIBRATE, vib.mUid, vib.mPackageName); + } if (mode != AppOpsManager.MODE_ALLOWED) { if (mode == AppOpsManager.MODE_ERRORED) { Slog.w(TAG, "Would be an error: vibrate from uid " + vib.mUid); diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index e2226aa..7c2de8b 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -206,10 +206,6 @@ public class NotificationManagerService extends SystemService { final ArrayList<NotificationScorer> mScorers = new ArrayList<NotificationScorer>(); private int mZenMode; - private int mPreZenAlarmVolume = -1; - private int mPreZenRingerMode = -1; - private boolean mZenMutingAlarm; - private boolean mZenMutingRinger; // temporary, until we update apps to provide metadata private static final Set<String> CALL_PACKAGES = new HashSet<String>(Arrays.asList( "com.google.android.dialer", @@ -1122,9 +1118,6 @@ public class NotificationManagerService extends SystemService { private final Uri ZEN_MODE = Settings.Global.getUriFor(Settings.Global.ZEN_MODE); - private final Uri MODE_RINGER - = Settings.Global.getUriFor(Settings.Global.MODE_RINGER); - SettingsObserver(Handler handler) { super(handler); } @@ -1137,8 +1130,6 @@ public class NotificationManagerService extends SystemService { false, this, UserHandle.USER_ALL); resolver.registerContentObserver(ZEN_MODE, false, this); - resolver.registerContentObserver(MODE_RINGER, - false, this); update(null); } @@ -1162,9 +1153,6 @@ public class NotificationManagerService extends SystemService { if (ZEN_MODE.equals(uri)) { updateZenMode(); } - if (MODE_RINGER.equals(uri)) { - updateRingerMode(); - } } } @@ -1230,7 +1218,6 @@ public class NotificationManagerService extends SystemService { Settings.Global.DEVICE_PROVISIONED, 0)) { mDisableNotificationAlerts = true; } - updateRingerMode(); updateZenMode(); // register for various Intents @@ -1694,10 +1681,6 @@ public class NotificationManagerService extends SystemService { pw.println(" mVibrateNotification=" + mVibrateNotification); pw.println(" mDisableNotificationAlerts=" + mDisableNotificationAlerts); pw.println(" mZenMode=" + Settings.Global.zenModeToString(mZenMode)); - pw.println(" mPreZenAlarmVolume=" + mPreZenAlarmVolume); - pw.println(" mPreZenRingerMode=" + mPreZenRingerMode); - pw.println(" mZenMutingAlarm=" + mZenMutingAlarm); - pw.println(" mZenMutingRinger=" + mZenMutingRinger); pw.println(" mSystemReady=" + mSystemReady); pw.println(" mArchive=" + mArchive.toString()); Iterator<StatusBarNotification> iter = mArchive.descendingIterator(); @@ -2023,7 +2006,7 @@ public class NotificationManagerService extends SystemService { useDefaultVibrate ? mDefaultVibrationPattern : mFallbackVibrationPattern, ((notification.flags & Notification.FLAG_INSISTENT) != 0) - ? 0: -1); + ? 0: -1, notification.audioStreamType); } finally { Binder.restoreCallingIdentity(identity); } @@ -2033,7 +2016,7 @@ public class NotificationManagerService extends SystemService { mVibrator.vibrate(r.sbn.getUid(), r.sbn.getBasePkg(), notification.vibrate, ((notification.flags & Notification.FLAG_INSISTENT) != 0) - ? 0: -1); + ? 0: -1, notification.audioStreamType); } } } @@ -2552,17 +2535,6 @@ public class NotificationManagerService extends SystemService { } } - private void updateRingerMode() { - final int ringerMode = Settings.Global.getInt(getContext().getContentResolver(), - Settings.Global.MODE_RINGER, -1); - final boolean nonSilentRingerMode = ringerMode == AudioManager.RINGER_MODE_NORMAL - || ringerMode == AudioManager.RINGER_MODE_VIBRATE; - if (mZenMode != Settings.Global.ZEN_MODE_OFF && nonSilentRingerMode) { - Settings.Global.putInt(getContext().getContentResolver(), - Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF); - } - } - private void updateZenMode() { final int mode = Settings.Global.getInt(getContext().getContentResolver(), Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF); @@ -2572,62 +2544,31 @@ public class NotificationManagerService extends SystemService { Settings.Global.zenModeToString(mode))); } mZenMode = mode; - if (mAudioManager != null) { - // call audio - final boolean muteCalls = mZenMode != Settings.Global.ZEN_MODE_OFF; - if (muteCalls) { - if (!mZenMutingRinger) { - if (DBG) Slog.d(TAG, "Muting STREAM_RING"); - mAudioManager.setStreamMute(AudioManager.STREAM_RING, true); - mZenMutingRinger = true; - } - // calls vibrate if ringer mode = vibrate, so set the ringer mode as well - final int ringerMode = mAudioManager.getRingerMode(); - if (ringerMode != AudioManager.RINGER_MODE_SILENT) { - if (DBG) Slog.d(TAG, "Saving ringer mode of " + ringerMode); - mPreZenRingerMode = ringerMode; - mAudioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT); - } - } else { - if (mZenMutingRinger) { - if (DBG) Slog.d(TAG, "Unmuting STREAM_RING"); - mAudioManager.setStreamMute(AudioManager.STREAM_RING, false); - mZenMutingRinger = false; - } - if (mPreZenRingerMode != -1) { - if (DBG) Slog.d(TAG, "Restoring ringer mode to " + mPreZenRingerMode); - mAudioManager.setRingerMode(mPreZenRingerMode); - mPreZenRingerMode = -1; - } - } - // alarm audio - final boolean muteAlarms = mZenMode == Settings.Global.ZEN_MODE_FULL; - if (muteAlarms) { - if (!mZenMutingAlarm) { - if (DBG) Slog.d(TAG, "Muting STREAM_ALARM"); - mAudioManager.setStreamMute(AudioManager.STREAM_ALARM, true); - mZenMutingAlarm = true; - } - // alarms don't simply respect mute, so set the volume as well - final int volume = mAudioManager.getStreamVolume(AudioManager.STREAM_ALARM); - if (volume != 0) { - if (DBG) Slog.d(TAG, "Saving STREAM_ALARM volume of " + volume); - mPreZenAlarmVolume = volume; - mAudioManager.setStreamVolume(AudioManager.STREAM_ALARM, 0, 0); - } - } else { - if (mZenMutingAlarm) { - if (DBG) Slog.d(TAG, "Unmuting STREAM_ALARM"); - mAudioManager.setStreamMute(AudioManager.STREAM_ALARM, false); - mZenMutingAlarm = false; - } - if (mPreZenAlarmVolume != -1) { - if (DBG) Slog.d(TAG, "Restoring STREAM_ALARM volume to " + mPreZenAlarmVolume); - mAudioManager.setStreamVolume(AudioManager.STREAM_ALARM, mPreZenAlarmVolume, 0); - mPreZenAlarmVolume = -1; - } - } - } + + final String[] exceptionPackages = null; // none (for now) + + // call restrictions + final boolean muteCalls = mZenMode != Settings.Global.ZEN_MODE_OFF; + mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.STREAM_RING, + muteCalls ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED, + exceptionPackages); + mAppOps.setRestriction(AppOpsManager.OP_PLAY_AUDIO, AudioManager.STREAM_RING, + muteCalls ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED, + exceptionPackages); + + // alarm restrictions + final boolean muteAlarms = mZenMode == Settings.Global.ZEN_MODE_FULL; + mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.STREAM_ALARM, + muteAlarms ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED, + exceptionPackages); + mAppOps.setRestriction(AppOpsManager.OP_PLAY_AUDIO, AudioManager.STREAM_ALARM, + muteAlarms ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED, + exceptionPackages); + + // restrict vibrations with no hints + mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.USE_DEFAULT_STREAM_TYPE, + (muteAlarms || muteCalls) ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED, + exceptionPackages); } private void updateRelatedUserCache(Context context) { |