java.lang.SecurityException: Not allowed to change Do Not Disturb state

一、基本知识介绍

在最近的开发工作中,涉及到调节媒体音量的功能,安卓中调节音量利用的AudioMananger类,AudioManager其实是一个系统服务,获取方法如下

(AudioManager) mContext.getSystemService(Service.AUDIO_SERVICE)

调节音量利用的api是

/**
     * Sets the volume index for a particular stream.
     * 

This method has no effect if the device implements a fixed volume policy * as indicated by {@link #isVolumeFixed()}. *

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()}. */ public void setStreamVolume(int streamType, int index, int flags)

streamType是声音的类型,AudioSystem.STREAM_MUSIC是音乐的,用来控制比如放歌、看视频的声音;AudioSystem.STREAM_RING是手机铃声,用来控制来电响铃的声音大小;AudioSystem.STREAM_ALARM是闹钟的,用来控制闹钟的响铃声量。

index,就是我们想要设置的声量;

flags,可以是AudioManager.FLAG_PLAY_SOUND,调音量的时候会发出声音;也可以是AudioManager.FLAG_SHOW_UI,调音量的时候会展示系统音量对话框,效果和按实体音量键一样;如果什么效果都不要求,可以传0.

 

二、权限介绍

到这里注意一点,就是在Android 7.0及之后,设置系统音量需要开启勿扰模式权限,需要在AndroidManifest.xml里面申请如下权限:

然后设置音量的时候,如果没有权限,需要引导用户去设置页打开勿扰模式下设置音量的权限:

NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && !notificationManager.isNotificationPolicyAccessGranted()) {
            startActivity(new Intent(Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS));
        }else{
            audioManager.setStreamVolume(AudioManager.STREAM_MUSIC,10,AudioManager.FLAG_SHOW_UI);
        }

 

三、Not allowed to change Do Not Disturb state问题分析与解决

java.lang.SecurityException: Not allowed to change Do Not Disturb state
        at android.os.Parcel.readException(Parcel.java:2013)
        at android.os.Parcel.readException(Parcel.java:1959)
        at android.media.IAudioService$Stub$Proxy.setStreamVolume(IAudioService.java:943)
        at android.media.AudioManager.setStreamVolume(AudioManager.java:1032)

这个异常出现的原因,就是没有申请第二节提到的权限,但是这个权限也不是百分百需要的,根据个人的经验这里详细说明一下出现该异常跟权限之间的关系:

(1)部分安卓机器上,不申请权限,设置音量诸如调大、调小、甚至静音以及静音之后再调大,不会出现这个异常。如在红米note8上面,即使勿扰模式没有打开,此时不考虑权限问题,甚至连权限注册都不用,notificationManager.isNotificationPolicyAccessGranted()返回的也是false,,直接调用setStreamVolume,没有异常抛出且设置有效。

(2)部分安卓机器上,不申请权限,调节音量变大变小不会抛异常,但是音量减小到0时,可能会抛此异常,所以在这样的机型上app如果需要支持音量调到静音,那么还得按照第二节中说的进行权限申请和判断;如果不需要调到静音,比如设一个最小值1而不是减到0,那么也可以不申请权限。

这种case下额外进行一下补充,也是我写本文的初衷,我遇到的机型就是这种case的,音量一减小到0,调用setStreamVolume就会抛异常,导致应用force stop,通过try catch来捕获异常吧,还是不能把音量设为0,而且我的测试机奇葩的地方在于系统不支持勿扰模式,也就是说权限申请都没用,最后通过查看AudioMananger的代码,发现了一个方法

@Deprecated
    public void setStreamMute(int streamType, boolean state) {
        Log.w(TAG, "setStreamMute is deprecated. adjustStreamVolume should be used instead.");
        int direction = state ? ADJUST_MUTE : ADJUST_UNMUTE;
        if (streamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
            adjustSuggestedStreamVolume(direction, streamType, 0);
        } else {
            adjustStreamVolume(streamType, direction, 0);
        }
    }

这个方法一看,带有@Deprecated注解,所以应该是Google不建议使用的,所以这个方法留意了好多次都没有去用它,最后找不到解决办法了,无意中用这个方法试了一下,居然没报异常并且成功设置成静音了。希望这个能帮助到一些朋友。

(3)部分安卓机型上,可能确实需要老老实实的申请权限,才能进行音量调节。

 

四、补充几个api

(1)关于音量调节的api,其实还有一个方法

public void adjustStreamVolume(int streamType, int direction, int flags)

这个方法第二个参数传的是一个方向,即音量是增大还是减小,不能传一个具体的音量,所以可能不如setStreamVolume灵活。

(2)获取当前某种类型的音量

public int getStreamVolume(int streamType)

(3)获取当前系统的某种类型的支持的最大音量

/**
     * Returns the maximum volume index for a particular stream.
     *
     * @param streamType The stream type whose maximum volume index is returned.
     * @return The maximum valid volume index for the stream.
     * @see #getStreamVolume(int)
     */
    public int getStreamMaxVolume(int streamType)

 

你可能感兴趣的:(Android,Android,AudioManaager)