Android T CEC AVC Feature

AVC是android T 在cec部分添加的一个feature。以盒子为例,它会在playback device cec状态更新时尝试去确认TV是否是支持<SET AUDIO VOLUME LEVEL>的,如果支持就更新该设备的avc flag,并在音量调节时通知TV进行音量更新。

和以前版本的区别,目前看就是在直接进行音量更新setStreamVolume时会发送<SET AUDIO VOLUME LEVEL> message进行直接更新,但是adjustStreamVolume仍然还是依赖<User Control Pressed>,这样的话作用就大打折扣。鉴于目前支持这个消息的机器应该也是几乎没有,暂时看意义比较有限。此外,android为了支持这个feature,在playback内部也增加了DeviceDiscovery的设计,也增加了cec bus的负担。

1.开机时就向AudioDeviceVolumeManager注册了OnDeviceVolumeBehaviorChangedListener

HdmiControlService.java

    @Override
    public void onBootPhase(int phase) {
        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
            mDisplayManager = getContext().getSystemService(DisplayManager.class);
            mTvInputManager = (TvInputManager) getContext().getSystemService(
                    Context.TV_INPUT_SERVICE);
            mPowerManager = new PowerManagerWrapper(getContext());
            mPowerManagerInternal = new PowerManagerInternalWrapper();
            mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
            mStreamMusicMaxVolume = getAudioManager().getStreamMaxVolume(AudioManager.STREAM_MUSIC);
            if (mAudioDeviceVolumeManager == null) {
                mAudioDeviceVolumeManager =
                        new AudioDeviceVolumeManagerWrapper(getContext());
            }
            getAudioDeviceVolumeManager().addOnDeviceVolumeBehaviorChangedListener(
                    mServiceThreadExecutor, this::onDeviceVolumeBehaviorChanged);
        } else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
            runOnServiceThread(this::bootCompleted);
        }
    }
    /**
     * @hide
     * Interface definition of a callback to be invoked when the volume behavior of an audio device
     * is updated.
     */
    public interface OnDeviceVolumeBehaviorChangedListener {
        /**
         * Called on the listener to indicate that the volume behavior of a device has changed.
         * @param device the audio device whose volume behavior changed
         * @param volumeBehavior the new volume behavior of the audio device
         */
        void onDeviceVolumeBehaviorChanged(
                @NonNull AudioDeviceAttributes device,
                @AudioManager.DeviceVolumeBehavior int volumeBehavior);
    }

这里后面再分析。

    /**
     * Listener for changes to the volume behavior of an audio output device. Caches the
     * volume behavior of devices used for Absolute Volume Control.
     */
    @VisibleForTesting
    @ServiceThreadOnly
    void onDeviceVolumeBehaviorChanged(AudioDeviceAttributes device, int volumeBehavior) {
        assertRunOnServiceThread();
        if (AVC_AUDIO_OUTPUT_DEVICES.contains(device)) {
            synchronized (mLock) {
                mAudioDeviceVolumeBehaviors.put(device, volumeBehavior);
            }
            checkAndUpdateAbsoluteVolumeControlState();
        }
    }
    // Audio output devices used for Absolute Volume Control
    private static final List<AudioDeviceAttributes> AVC_AUDIO_OUTPUT_DEVICES =
            Collections.unmodifiableList(Arrays.asList(AUDIO_OUTPUT_DEVICE_HDMI,
                    AUDIO_OUTPUT_DEVICE_HDMI_ARC, AUDIO_OUTPUT_DEVICE_HDMI_EARC));

PLAYBACK相关的AUDIO_OUTPUT_DEVICE_HDMI,TV类型的AUDIO_OUTPUT_DEVICE_HDMI_ARC, AUDIO_OUTPUT_DEVICE_HDMI_EARC都属于AVC设备。

当系统的AVC设备对应的volume behaviour发生变更时,就会通过checkAndUpdateAbsoluteVolumeControlState来检查和更新HdmiControlService内部的状态。

2. 进行volume control behavior更新的时间。

监听cec status的设计在T上这里的实现再次发生变更,直接通过DEVICE_OUT_HDMI的volume behaviour的方式来更新cec sink device的状态。

首先如果DEVICE_OUT_HDMI设备有过AudioService的setDeviceVolumeBehavior接口设置过behaviour的话,就不再更新了。

这里有一个问题,如果cec不可用了,然后也设置过FULL的behaviour的话,盒子的音量键将完全失去作用。

    //==========================================================================================
    // Hdmi CEC:
    // - System audio mode:
    //     If Hdmi Cec's system audio mode is on, audio service should send the volume change
    //     to HdmiControlService so that the audio receiver can handle it.
    // - CEC sink:
    //     OUT_HDMI becomes a "full volume device", i.e. output is always at maximum level
    //     and volume changes won't be taken into account on this device. Volume adjustments
    //     are transformed into key events for the HDMI playback client.
    //==========================================================================================

    @GuardedBy("mHdmiClientLock")
    private void updateHdmiCecSinkLocked(boolean hdmiCecSink) {
        if (!hasDeviceVolumeBehavior(AudioSystem.DEVICE_OUT_HDMI)) {
            if (hdmiCecSink) {
                if (DEBUG_VOL) {
                    Log.d(TAG, "CEC sink: setting HDMI as full vol device");
                }
                setDeviceVolumeBehaviorInternal(
                        new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_HDMI, ""),
                        AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL,
                        "AudioService.updateHdmiCecSinkLocked()");
            } else {
                if (DEBUG_VOL) {
                    Log.d(TAG, "TV, no CEC: setting HDMI as regular vol device");
                }
                // Android TV devices without CEC service apply software volume on
                // HDMI output
                setDeviceVolumeBehaviorInternal(
                        new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_HDMI, ""),
                        AudioManager.DEVICE_VOLUME_BEHAVIOR_VARIABLE,
                        "AudioService.updateHdmiCecSinkLocked()");
            }
            postUpdateVolumeStatesForAudioDevice(AudioSystem.DEVICE_OUT_HDMI,
                    "HdmiPlaybackClient.DisplayStatusCallback");
        }
    }

只有persistDeviceVolumeBehavior才会将volume behaviour记录到system settings里面,比如

_id:36 name:AudioService_DeviceVolumeBehavior_hdmi pkg:android value:1 default:1 defaultSystemSet:true

    public void setDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device,
            @AudioManager.DeviceVolumeBehavior int deviceVolumeBehavior, @Nullable String pkgName) {
        // verify permissions
        enforceModifyAudioRoutingPermission();
        // verify arguments
        Objects.requireNonNull(device);
        AudioManager.enforceValidVolumeBehavior(deviceVolumeBehavior);
        sVolumeLogger.log(new AudioEventLogger.StringEvent("setDeviceVolumeBehavior: dev:"
                + AudioSystem.getOutputDeviceName(device.getInternalType()) + " addr:"
                + device.getAddress() + " behavior:"
                + AudioDeviceVolumeManager.volumeBehaviorName(deviceVolumeBehavior)
                + " pack:" + pkgName).printLog(TAG));
        if (pkgName == null) {
            pkgName = "";
        }
        if (device.getType() == AudioDeviceInfo.TYPE_BLUETOOTH_A2DP) {
            avrcpSupportsAbsoluteVolume(device.getAddress(),
                    deviceVolumeBehavior == AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
            return;
        }

        setDeviceVolumeBehaviorInternal(device, deviceVolumeBehavior, pkgName);
        persistDeviceVolumeBehavior(device.getInternalType(), deviceVolumeBehavior);
    }

AudioService在设置MyHdmiControlStatusChangeListenerCallback时必然会调用updateHdmiCecSinkLocked来更新一次volume behaviour。

    @VisibleForTesting
    void addHdmiControlStatusChangeListener(
            final IHdmiControlStatusChangeListener listener) {
        final HdmiControlStatusChangeListenerRecord record =
                new HdmiControlStatusChangeListenerRecord(listener);
        try {
            listener.asBinder().linkToDeath(record, 0);
        } catch (RemoteException e) {
            Slog.w(TAG, "Listener already died");
            return;
        }
        synchronized (mLock) {
            mHdmiControlStatusChangeListenerRecords.add(record);
        }

        // Inform the listener of the initial state of each HDMI port by generating
        // hotplug events.
        runOnServiceThread(new Runnable() {
            @Override
            public void run() {
                synchronized (mLock) {
                    if (!mHdmiControlStatusChangeListenerRecords.contains(record)) return;
                }

                // Return the current status of mHdmiControlEnabled;
                synchronized (mLock) {
                    invokeHdmiControlStatusChangeListenerLocked(listener, mHdmiControlEnabled);
                }
            }
        });
    }

    private class MyHdmiControlStatusChangeList
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值