56

I have an app that mutes the phone by using AudioManager and setting ringer mode to silent with this code:

AudioManager audioManager = 
    (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
try {
    audioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT)
} catch (Exception e) {
    e.printStackTrace();
}

This works with Android 6, but now with Android 7, I get the following error:

System.err: java.lang.SecurityException: Not allowed to change Do Not Disturb state
System.err: at android.os.Parcel.readException(Parcel.java:1683)
System.err: at android.os.Parcel.readException(Parcel.java:1636)
System.err: at android.media.IAudioService$Stub$Proxy.setRingerModeExternal(IAudioService.java:962)
System.err: at android.media.AudioManager.setRingerMode(AudioManager.java:1022)
System.err: at controllers.SearchResultController.mutePhone(SearchResultController.java:185)

Are there any new permissions I need to ask for to make this work?

I looked through the Android permissions list, but couldn't find any that seemed relevant.

hichris123
  • 10,145
  • 15
  • 56
  • 70
Bjarte Aune Olsen
  • 3,230
  • 4
  • 24
  • 36

8 Answers8

85

Thanks for your answers, here is a little more detail.

To be able to set ringer mode to silent, you must ask permission to access notification policy (like @ucsunil said).

<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" />

Then, check if you have this permission. If you do not, open the settings for "Do Not Disturb access" for your app:

NotificationManager notificationManager = 
    (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
    && !notificationManager.isNotificationPolicyAccessGranted()) {

    Intent intent = new Intent(
                        android.provider.Settings
                        .ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS);

    startActivity(intent);
}

When you run startActivity(), Android opens the Do Not Disturb access settings for your app.

What confused me, was that the way to ask for this permission is completely different from other permissions.

Just for reference, here is the way to ask for permission READ_CONTACTS:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
    && ActivityCompat.checkSelfPermission(activity,
        Manifest.permission.READ_CONTACTS)
        == PackageManager.PERMISSION_DENIED) {

    ActivityCompat.requestPermissions(activity,
        new String[]{ Manifest.permission.READ_CONTACTS },
            REQUEST_CODE_READ_CONTACTS);
}
JM Lord
  • 1,031
  • 1
  • 13
  • 29
Bjarte Aune Olsen
  • 3,230
  • 4
  • 24
  • 36
  • 6
    Do Not Disturb access is from SDK 23 Marshmallow. So `>= Build.VERSION_CODES.M` BTW is necessary to `` – Ricardo Pessoa Nov 07 '16 at 12:58
  • 7
    It's amazing how complicated simple things tend to get with Android. This is the solution though.. – milosmns Aug 23 '17 at 09:42
  • 1
    @RicardoPessoa: When running my app on a virtual phone with Android M (23), I do not need to ask for the Do Not Disturb permission to be able to mute ringing, only on Android N (24). – Bjarte Aune Olsen Nov 29 '17 at 21:46
  • @RicardoPessoa: Sorry, my mistake. When you mute the phone with audioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT);, you don't need this permission in Android M, but when you mute the phone with notificationManager.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_NONE);, which works in Android M and onward, you need to ask for this permission. Updated my answer above. – Bjarte Aune Olsen Nov 29 '17 at 22:41
  • In my case my phone never bother with that but at some point it started to throw that exception. it was very odd, but I am glad to know others have found this solution – Juan Mendez Feb 03 '18 at 04:43
11

The access you are looking for is:

<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" />

You need to request the user to provide you with this specific permission.

ucsunil
  • 7,378
  • 1
  • 27
  • 32
6

ACCESS_NOTIFICATION_POLICY is considered a "normal" permission which means you do NOT have to request permission at runtime. You only have to add the permission to AndroidManifest.xml

    <uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" />

Then, to mute all notifications you can set the Interruption filter to INTERRUPTION_FILTER_NONE

NotificationManager notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            previous_notification_interrupt_setting = notificationManager.getCurrentInterruptionFilter();
            notificationManager.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_NONE);
        }

INTERRUPTION_FILTER_ALARMS - Alarms only
INTERRUPTION_FILTER_ALL - Normal filter
INTERRUPTION_FILTER_NONE - No interruptions (MUTE)
INTERRUPTION_FILTER_PRIORITY - Priority interruptions

I use

 int previous_notification_interrupt_setting = notificationManager.getCurrentInterruptionFilter();

to reset the filter later when onPause() or onStop()

  • Thanks, I'll try this when I have the chance. I think I already had the ACCESS_NOTIFICATION_POLICY in AndroidManifest.xml when I got the mentioned error, but I noticed I use a different method for turning off notifications (audioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT)), which might be outdated. I will try using notificationManager instead and see if it works better. – Bjarte Aune Olsen Oct 08 '16 at 18:24
  • I finally got around to implementing notificationManager.setInterruptionFilter() for Android versions M and onwards. Thanks! Because of this I had to start checking for isNotificationPolicyAccessGranted() also for Android M (not just N, that I stated in my original answer). – Bjarte Aune Olsen Nov 29 '17 at 22:37
3

I've resolved this issue by opening Setting menu and asking user to grant permission for it if api > 23.

Just call requestMutePermissions() from anywhere and it will work!

private void requestMutePermissions() {
    try {
        if (Build.VERSION.SDK_INT < 23) {
            AudioManager audioManager = (AudioManager)getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
            audioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT);
        } else if( Build.VERSION.SDK_INT >= 23 ) {
            this.requestForDoNotDisturbPermissionOrSetDoNotDisturbForApi23AndUp();
        }
    } catch ( SecurityException e ) {

    }
}

private void requestForDoNotDisturbPermissionOrSetDoNotDisturbForApi23AndUp() {

    NotificationManager notificationManager = (NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
    // if user granted access else ask for permission
    if ( notificationManager.isNotificationPolicyAccessGranted()) {
        AudioManager audioManager = (AudioManager) getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
        audioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT);
    } else{
        // Open Setting screen to ask for permisssion
        Intent intent = new Intent(android.provider.Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS);
        startActivityForResult( intent, ON_DO_NOT_DISTURB_CALLBACK_CODE );
    }
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    // Check which request we're responding to
    if (requestCode == ON_DO_NOT_DISTURB_CALLBACK_CODE ) {
        this.requestForDoNotDisturbPermissionOrSetDoNotDisturbForApi23AndUp();
    }
}
Waqar UlHaq
  • 6,144
  • 2
  • 34
  • 42
  • Is there a way to take the user directly to the activity for your app, instead of the activity for all apps? I'd like to reduce the number of steps our users have to take in order to enable the permission. – TALE Sep 01 '20 at 19:16
  • @TALE I did see an app that flashes and highlights the specific line in that window that you need to toggle, for the app requesting the permission, so at least that much seems possible. – gamingexpert13 Jul 30 '21 at 19:18
1

Based on Android documentation, you have to grant access to Do Not Disturb.

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 isNotificationPolicyAccessGranted().

Please check the link

Hope it helps!

Lucas Santos
  • 150
  • 6
1

Hy, If anyone needs this permission in Kotlin, here you are

var notificationManager = this.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
        && !notificationManager.isNotificationPolicyAccessGranted
    ) {
        val intent = Intent(
            Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS
        )
        startActivity(intent)
    }

I really appreciate your advices that was life saving! :D

0

I faced this issue only in an Emulator. When i run the app on my mobile it never comes and it works as expected.

UzumakiL
  • 373
  • 3
  • 9
-1

In an Android device you have to grant DoNotDisturb access to your app

Dharman
  • 30,962
  • 25
  • 85
  • 135