import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
+import android.app.NotificationManager;
import android.app.PendingIntent;
import android.bluetooth.BluetoothDevice;
import android.content.ComponentName;
* according to user settings.
* <p>This method has no effect if the device implements a fixed volume policy
* as indicated by {@link #isVolumeFixed()}.
+ * * <p>From N onward, ringer mode adjustments that would toggle Do Not Disturb are not allowed
+ * unless the app has been granted Do Not Disturb Access.
+ * See {@link NotificationManager#isNotificationPolicyAccessGranted()}.
* @param ringerMode The ringer mode, one of {@link #RINGER_MODE_NORMAL},
* {@link #RINGER_MODE_SILENT}, or {@link #RINGER_MODE_VIBRATE}.
* @see #getRingerMode()
* Sets the volume index for a particular stream.
* <p>This method has no effect if the device implements a fixed volume policy
* as indicated by {@link #isVolumeFixed()}.
+ * <p>From N onward, volume adjustments that would toggle Do Not Disturb are not allowed unless
+ * the app has been granted Do Not Disturb Access.
+ * See {@link NotificationManager#isNotificationPolicyAccessGranted()}.
* @param streamType The stream whose volume index should be set.
* @param index The volume index to set. See
* {@link #getStreamMaxVolume(int)} for the largest valid value.
* <p>
* This method has no effect if the device implements a fixed volume policy
* as indicated by {@link #isVolumeFixed()}.
+ * <p>From N onward, stream mute changes that would toggle Do Not Disturb are not allowed unless
+ * the app has been granted Do Not Disturb Access.
+ * See {@link NotificationManager#isNotificationPolicyAccessGranted()}.
* <p>
* This method was deprecated in API level 22. Prior to API level 22 this
* method had significantly different behavior and should be used carefully.
import android.app.ActivityManagerNative;
import android.app.AppGlobals;
import android.app.AppOpsManager;
+import android.app.NotificationManager;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.util.Slog;
import android.util.SparseIntArray;
import android.view.KeyEvent;
-import android.view.OrientationEventListener;
-import android.view.Surface;
-import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
import com.android.internal.util.XmlUtils;
private static Long mLastDeviceConnectMsgTime = new Long(0);
+ private NotificationManager mNm;
private AudioManagerInternal.RingerModeDelegate mRingerModeDelegate;
private VolumePolicy mVolumePolicy = VolumePolicy.DEFAULT;
private long mLoweredFromNormalToVibrateTime;
}
}
+ mNm = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+
sendMsg(mAudioHandler,
MSG_CONFIGURE_SAFE_MEDIA_VOLUME_FORCED,
SENDMSG_REPLACE,
// Check if the ringer mode handles this adjustment. If it does we don't
// need to adjust the volume further.
final int result = checkForRingerModeChange(aliasIndex, direction, step,
- streamState.mIsMuted);
+ streamState.mIsMuted, caller);
adjustVolume = (result & FLAG_ADJUST_VOLUME) != 0;
// If suppressing a volume adjustment in silent mode, display the UI hint
if ((result & AudioManager.FLAG_SHOW_SILENT_HINT) != 0) {
&& (mRingerModeMutedStreams & (1 << AudioSystem.STREAM_MUSIC)) != 0) {
adjustVolume = false;
}
-
int oldIndex = mStreamStates[streamType].getIndex(device);
if (adjustVolume && (direction != AudioManager.ADJUST_SAME)) {
}
};
- private void onSetStreamVolume(int streamType, int index, int flags, int device,
- String caller) {
- final int stream = mStreamVolumeAlias[streamType];
- setStreamVolumeInt(stream, index, device, false, caller);
+ private int getNewRingerMode(int stream, int index, int flags) {
// setting volume on ui sounds stream type also controls silent mode
if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
(stream == getUiSoundsStreamType())) {
if (index == 0) {
newRingerMode = mHasVibrator ? AudioManager.RINGER_MODE_VIBRATE
: mVolumePolicy.volumeDownToEnterSilent ? AudioManager.RINGER_MODE_SILENT
- : AudioManager.RINGER_MODE_NORMAL;
+ : AudioManager.RINGER_MODE_NORMAL;
} else {
newRingerMode = AudioManager.RINGER_MODE_NORMAL;
}
- setRingerMode(newRingerMode, TAG + ".onSetStreamVolume", false /*external*/);
+ return newRingerMode;
+ }
+ return getRingerModeExternal();
+ }
+
+ private boolean isAndroidNPlus(String caller) {
+ try {
+ final ApplicationInfo applicationInfo =
+ mContext.getPackageManager().getApplicationInfoAsUser(
+ caller, 0, UserHandle.getUserId(Binder.getCallingUid()));
+ if (applicationInfo.targetSdkVersion >= Build.VERSION_CODES.N) {
+ return true;
+ }
+ return false;
+ } catch (PackageManager.NameNotFoundException e) {
+ return true;
+ }
+ }
+
+ private boolean wouldToggleZenMode(int newMode) {
+ if (getRingerModeExternal() == AudioManager.RINGER_MODE_SILENT
+ && newMode != AudioManager.RINGER_MODE_SILENT) {
+ return true;
+ } else if (getRingerModeExternal() != AudioManager.RINGER_MODE_SILENT
+ && newMode == AudioManager.RINGER_MODE_SILENT) {
+ return true;
+ }
+ return false;
+ }
+
+ private void onSetStreamVolume(int streamType, int index, int flags, int device,
+ String caller) {
+ final int stream = mStreamVolumeAlias[streamType];
+ setStreamVolumeInt(stream, index, device, false, caller);
+ // setting volume on ui sounds stream type also controls silent mode
+ if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
+ (stream == getUiSoundsStreamType())) {
+ setRingerMode(getNewRingerMode(stream, index, flags),
+ TAG + ".onSetStreamVolume", false /*external*/);
}
// setting non-zero volume for a muted stream unmutes the stream and vice versa
mStreamStates[stream].mute(index == 0);
return;
}
+ if (isAndroidNPlus(callingPackage)
+ && wouldToggleZenMode(getNewRingerMode(streamTypeAlias, index, flags))
+ && !mNm.isNotificationPolicyAccessGrantedForPackage(callingPackage)) {
+ throw new SecurityException("Not allowed to change Do Not Disturb state");
+ }
+
synchronized (mSafeMediaVolumeState) {
// reset any pending volume command
mPendingVolumeCommand = null;
}
public void setRingerModeExternal(int ringerMode, String caller) {
+ if (isAndroidNPlus(caller) && wouldToggleZenMode(ringerMode)
+ && !mNm.isNotificationPolicyAccessGrantedForPackage(caller)) {
+ throw new SecurityException("Not allowed to change Do Not Disturb state");
+ }
+
setRingerMode(ringerMode, caller, true /*external*/);
}
* adjusting volume. If so, this will set the proper ringer mode and volume
* indices on the stream states.
*/
- private int checkForRingerModeChange(int oldIndex, int direction, int step, boolean isMuted) {
+ private int checkForRingerModeChange(int oldIndex, int direction, int step, boolean isMuted,
+ String caller) {
final boolean isTv = mPlatformType == AudioSystem.PLATFORM_TELEVISION;
int result = FLAG_ADJUST_VOLUME;
int ringerMode = getRingerModeInternal();
break;
}
+ if (isAndroidNPlus(caller) && wouldToggleZenMode(ringerMode)
+ && !mNm.isNotificationPolicyAccessGrantedForPackage(caller)) {
+ throw new SecurityException("Not allowed to change Do Not Disturb state");
+ }
+
setRingerMode(ringerMode, TAG + ".checkForRingerModeChange", false /*external*/);
mPrevVolDirection = direction;