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
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.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.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 @@
= 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 @@
}
@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 @@
}
}
}
+ 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 @@
}
}
}
+
+ 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 @@
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 @@
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 @@
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 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 @@
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 @@
// 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 @@
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 @@
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 @@
false, this, UserHandle.USER_ALL);
resolver.registerContentObserver(ZEN_MODE,
false, this);
- resolver.registerContentObserver(MODE_RINGER,
- false, this);
update(null);
}
@@ -1162,9 +1153,6 @@
if (ZEN_MODE.equals(uri)) {
updateZenMode();
}
- if (MODE_RINGER.equals(uri)) {
- updateRingerMode();
- }
}
}
@@ -1230,7 +1218,6 @@
Settings.Global.DEVICE_PROVISIONED, 0)) {
mDisableNotificationAlerts = true;
}
- updateRingerMode();
updateZenMode();
// register for various Intents
@@ -1694,10 +1681,6 @@
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 @@
useDefaultVibrate ? mDefaultVibrationPattern
: mFallbackVibrationPattern,
((notification.flags & Notification.FLAG_INSISTENT) != 0)
- ? 0: -1);
+ ? 0: -1, notification.audioStreamType);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -2033,7 +2016,7 @@
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 @@
}
}
- 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 @@
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) {