Android AudioService安全音量设置逻辑

问题点描述:
还原出厂设置后,第一次启动安卓系统会自动降音量设成10,导致开机时音量不一致问题。

原因:安全音量逻辑将音量设置为10

安全音量配置和音量值
\frameworks\base\core\res\res\values\config.xml

    <!-- Safe headphone volume index. When music stream volume is below this index
    the SPL on headphone output is compliant to EN 60950 requirements for portable music
    players. -->
    <integer name="config_safe_media_volume_index">10</integer>

    <!-- Whether safe headphone volume is enabled or not (country specific). -->
    <bool name="config_safe_media_volume_enabled">true</bool>

安全音量逻辑:
frameworks\base\services\core\java\com\android\server\audio\AudioService.java

// mSafeMediaVolumeDevices lists the devices for which safe media volume is enforced,
private final int mSafeMediaVolumeDevices = AudioSystem.DEVICE_OUT_WIRED_HEADSET |
                                           	AudioSystem.DEVICE_OUT_WIRED_HEADPHONE |
                                           	AudioSystem.DEVICE_OUT_USB_HEADSET;


///
// Construction
///
/** @hide */
public AudioService(Context context) {
	//...
	
    // The default safe volume index read here will be replaced by the actual value when
    // the mcc is read by onConfigureSafeVolume()
    mSafeMediaVolumeIndex = mContext.getResources().getInteger(
            com.android.internal.R.integer.config_safe_media_volume_index) * 10;
	
	//...
}

public void onSystemReady() {
	//...
    sendMsg(mAudioHandler,
            MSG_CONFIGURE_SAFE_MEDIA_VOLUME_FORCED,
            SENDMSG_REPLACE,
            0,
            0,
            TAG,
            SystemProperties.getBoolean("audio.safemedia.bypass", false) ?
                    0 : SAFE_VOLUME_CONFIGURE_TIMEOUT_MS);
	//...
}

/** Handles internal volume messages in separate volume thread. */
private class AudioHandler extends Handler {
	//...
	@Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case MSG_CONFIGURE_SAFE_MEDIA_VOLUME_FORCED:
            case MSG_CONFIGURE_SAFE_MEDIA_VOLUME:
                onConfigureSafeVolume((msg.what == MSG_CONFIGURE_SAFE_MEDIA_VOLUME_FORCED),
                        (String) msg.obj);
                break;
        }
    }
	//...
}

private void onConfigureSafeVolume(boolean force, String caller) {
    synchronized (mSafeMediaVolumeState) {
        int mcc = mContext.getResources().getConfiguration().mcc;
        if ((mMcc != mcc) || ((mMcc == 0) && force)) {
            mSafeMediaVolumeIndex = mContext.getResources().getInteger(
                    com.android.internal.R.integer.config_safe_media_volume_index) * 10;

            mSafeUsbMediaVolumeIndex = getSafeUsbMediaVolumeIndex();

            boolean safeMediaVolumeEnabled =
                    SystemProperties.getBoolean("audio.safemedia.force", false)
                    || mContext.getResources().getBoolean(
                            com.android.internal.R.bool.config_safe_media_volume_enabled);

            boolean safeMediaVolumeBypass =
                    SystemProperties.getBoolean("audio.safemedia.bypass", false);

            // The persisted state is either "disabled" or "active": this is the state applied
            // next time we boot and cannot be "inactive"
            int persistedState;
            if (safeMediaVolumeEnabled && !safeMediaVolumeBypass) {
                persistedState = SAFE_MEDIA_VOLUME_ACTIVE;
                // The state can already be "inactive" here if the user has forced it before
                // the 30 seconds timeout for forced configuration. In this case we don't reset
                // it to "active".
                if (mSafeMediaVolumeState != SAFE_MEDIA_VOLUME_INACTIVE) {
                    if (mMusicActiveMs == 0) {
                        mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_ACTIVE;
                        enforceSafeMediaVolume(caller);
                    } else {
                        // We have existing playback time recorded, already confirmed.
                        mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_INACTIVE;
                    }
                }
            } else {
                persistedState = SAFE_MEDIA_VOLUME_DISABLED;
                mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_DISABLED;
            }
            mMcc = mcc;
            sendMsg(mAudioHandler,
                    MSG_PERSIST_SAFE_VOLUME_STATE,
                    SENDMSG_QUEUE,
                    persistedState,
                    0,
                    null,
                    0);
        }
    }
}

private void enforceSafeMediaVolume(String caller) {
    VolumeStreamState streamState = mStreamStates[AudioSystem.STREAM_MUSIC];
    int devices = mSafeMediaVolumeDevices;
    int i = 0;

    while (devices != 0) {
        int device = 1 << i++;
        if ((device & devices) == 0) {
            continue;
        }
        int index = streamState.getIndex(device);
        if (index > safeMediaVolumeIndex(device)) {
            streamState.setIndex(safeMediaVolumeIndex(device), device, caller);
            sendMsg(mAudioHandler,
                    MSG_SET_DEVICE_VOLUME,
                    SENDMSG_QUEUE,
                    device,
                    0,
                    streamState,
                    0);
        }
        devices &= ~device;
    }
}

private int safeMediaVolumeIndex(int device) {
    if ((device & mSafeMediaVolumeDevices) == 0) {
        return MAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC];
    }
    if (device == AudioSystem.DEVICE_OUT_USB_HEADSET) {
        return mSafeUsbMediaVolumeIndex;
    } else {
        return mSafeMediaVolumeIndex;
    }
}

config_safe_media_volume_enabled的配置是一个关键,决定了安全音量的功能是否有效。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
好的,让我来逐行分析下Android T的AudioService.java中setMode函数。 ```java /** * @see AudioManager#setMode(int) */ public void setMode(int mode, IBinder cb, String callingPackageName) { enforceModifyAudioRoutingPermission(); synchronized (mConnectedDevices) { final int newModeOwnerPid = Binder.getCallingPid(); final int newModeOwnerUid = Binder.getCallingUid(); final long ident = Binder.clearCallingIdentity(); final boolean modeOwnerChanged = (mModeOwnerPid != newModeOwnerPid || mModeOwnerUid != newModeOwnerUid); try { setModeInt(mode, cb, callingPackageName, modeOwnerChanged, newModeOwnerPid, newModeOwnerUid); } finally { Binder.restoreCallingIdentity(ident); } } } ``` 这个函数的作用是设置音频模式,它是实现了AudioManager类的setMode方法。 函数的第一个参数mode是要设置的音频模式。 函数的第二个参数cb是一个Binder对象,它可以在AudioFocusManager中使用,用来管理音频焦点。 函数的第三个参数callingPackageName是调用这个函数的应用程序的包名。 接下来是函数的实现。 首先,它调用enforceModifyAudioRoutingPermission()方法来检查是否有修改音频路由的权限。 然后,它使用synchronized关键字同步了mConnectedDevices,这是一个存储连接的设备信息的集合。 接着,它获取了调用这个函数的应用程序的PID和UID,以及清除了调用者的身份验证标识符。 接下来,它检查是否有新的模式所有者,并设置了一个用于检测模式所有者更改的标志。 最后,它调用了setModeInt方法来实际设置音频模式,传递了所有必要的参数,并在结束时恢复了调用者的身份验证标识符。 至此,Android T的AudioService.java中setMode函数的分析就结束了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sunxiaolin2016

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值