ARTICLE DETAIL

资讯详情

深耕网站SEO优化与搜索引擎排名提升的一线实战洞察。

Android16 连接ARC,遥控器调节音量,ARC会静音一下再有声音

Android16 连接ARC,遥控器调节音量,ARC会静音一下再有声音 由于在HdmiCecController中有添加自定义的逻辑先查看相关log分析一下。可以看到在发送了Volume up之后立刻又发送了Volume mute而我们实际操作只按了一下音量加不符合我们的操作这里往上追了一下发现方法有很多地方都有调用这里从按键触发音量变化开始追。这里大概AI了一下具体调用流程看到adjustStreamVolume我们的自定义音量条也是通过这个方法来调整音量的因此直接从这里开始分析。[内核 input] KEY_VOLUMEUP ↓ [InputManagerService] 分发 KeyEvent ↓ [DecorView.dispatchKeyEvent()] → Activity 未消费 ↓ [PhoneWindow.onKeyDown()] ↓ [MediaSessionManager.dispatchVolumeKeyEventAsSystemService()] ↓ (Binder) [MediaSessionService.dispatchAdjustVolumeLocked()] ↓ [AudioService.adjustSuggestedStreamVolume()] ← 确定流类型 ↓ [AudioService.adjustStreamVolume()] ← 别名映射 步长计算 ↓ (Handler 消息) [AudioService.setDeviceVolume() → applyDeviceVolume_syncVSS()] ↓ [AudioSystem.setStreamVolumeIndex()] (JNI) ↓ [AudioPolicyManager::setStreamVolumeIndex()] ↓ [AudioFlinger::setStreamVolume()] → PlaybackThread.mStreamTypes[stream].volume ↓ [混音器下次 mix 时乘以新 volume] ← 声音真正变大 ✅ ↘ 同时[VolumeController] → SystemUI 音量条弹出/** Retain API for unsupported app usage */ public void adjustStreamVolume(int streamType, int direction, int flags, String callingPackage) { adjustStreamVolumeWithAttribution(streamType, direction, flags, callingPackage, null); } /** see AudioManager#adjustStreamVolume(int, int, int) * Part of service interface, check permissions here */ public void adjustStreamVolumeWithAttribution(int streamType, int direction, int flags, String callingPackage, String attributionTag) { if (mHardeningEnforcer.blockVolumeMethod( HardeningEnforcer.METHOD_AUDIO_MANAGER_ADJUST_STREAM_VOLUME, callingPackage, Binder.getCallingUid())) { return; } if ((streamType AudioManager.STREAM_ACCESSIBILITY) !canChangeAccessibilityVolume()) { Log.w(TAG, Trying to call adjustStreamVolume() for a11y without CHANGE_ACCESSIBILITY_VOLUME / callingPackage callingPackage); return; } final VolumeEvent evt new VolumeEvent(VolumeEvent.VOL_ADJUST_STREAM_VOL, streamType, direction/*val1*/, flags/*val2*/, callingPackage); sVolumeLogger.enqueue(evt); // also logging mute/unmute calls to the dedicated logger if (isMuteAdjust(direction)) { sMuteLogger.enqueue(evt); } adjustStreamVolume(streamType, direction, flags, callingPackage, callingPackage, Binder.getCallingUid(), Binder.getCallingPid(), attributionTag, callingHasAudioSettingsPermission(), AudioDeviceVolumeManager.ADJUST_MODE_NORMAL); } protected void adjustStreamVolume(int streamType, int direction, int flags, String callingPackage, String caller, int uid, int pid, String attributionTag, boolean hasModifyAudioSettings, int keyEventMode) { if (mUseFixedVolume) { return; } streamType replaceBtScoStreamWithVoiceCall(streamType, adjustStreamVolume); if (DEBUG_VOL) Log.d(TAG, adjustStreamVolume() stream streamType , dir direction , flags flags , caller caller); ensureValidDirection(direction); ensureValidStreamType(streamType); boolean isMuteAdjust isMuteAdjust(direction); if (isMuteAdjust !isStreamAffectedByMute(streamType)) { return; } // If adjust is mute and the stream is STREAM_VOICE_CALL or STREAM_BLUETOOTH_SCO, make sure // that the calling app have the MODIFY_PHONE_STATE permission. if (isMuteAdjust (streamType AudioSystem.STREAM_VOICE_CALL || // TODO: when replaceStreamBtSco flag is rolled out remove next condition isStreamBluetoothSco(streamType)) mContext.checkPermission(MODIFY_PHONE_STATE, pid, uid) ! PackageManager.PERMISSION_GRANTED) { Log.w(TAG, MODIFY_PHONE_STATE Permission Denial: adjustStreamVolume from pid Binder.getCallingPid() , uid Binder.getCallingUid()); return; } // If the stream is STREAM_ASSISTANT, // make sure that the calling app have the MODIFY_AUDIO_ROUTING permission. if (streamType AudioSystem.STREAM_ASSISTANT mContext.checkPermission( MODIFY_AUDIO_ROUTING, pid, uid) ! PackageManager.PERMISSION_GRANTED) { Log.w(TAG, MODIFY_AUDIO_ROUTING Permission Denial: adjustStreamVolume from pid Binder.getCallingPid() , uid Binder.getCallingUid()); return; } // use stream type alias here so that streams with same alias have the same behavior, // including with regard to silent mode control (e.g the use of STREAM_RING below and in // checkForRingerModeChange() in place of STREAM_RING or STREAM_NOTIFICATION) int streamTypeAlias sStreamVolumeAlias.get(streamType, /*valueIfKeyNotFound*/-1); if (streamTypeAlias -1) { Log.e(TAG, adjustStreamVolume: no stream vol alias for stream type streamType); } VolumeStreamState streamState getVssForStreamOrDefault(streamTypeAlias); final int device getDeviceForStream(streamTypeAlias); int aliasIndex streamState.getIndex(device); boolean adjustVolume true; int step; // skip a2dp absolute volume control request when the device // is neither an a2dp device nor BLE device if ((!AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device) !AudioSystem.DEVICE_OUT_ALL_BLE_SET.contains(device)) (flags AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) ! 0) { return; } // If we are being called by the system (e.g. hardware keys) check for current user // so we handle user restrictions correctly. if (uid android.os.Process.SYSTEM_UID) { uid UserHandle.getUid(getCurrentUserId(), UserHandle.getAppId(uid)); } // validate calling package and app op if (!checkNoteAppOp( STREAM_VOLUME_OPS[streamTypeAlias], uid, callingPackage, attributionTag)) { return; } mSoundDoseHelper.invalidatePendingVolumeCommand(); flags ~AudioManager.FLAG_FIXED_VOLUME; if (streamTypeAlias AudioSystem.STREAM_MUSIC isFixedVolumeDevice(device)) { flags | AudioManager.FLAG_FIXED_VOLUME; // Always toggle between max safe volume and 0 for fixed volume devices where safe // volume is enforced, and max and 0 for the others. // This is simulated by stepping by the full allowed volume range step mSoundDoseHelper.getSafeMediaVolumeIndex(device); if (step 0) { step streamState.getMaxIndex(); } if (aliasIndex ! 0) { aliasIndex step; } } else { // convert one UI step (/-1) into a number of internal units on the stream alias step rescaleStep((int) (10 * streamState.getIndexStepFactor()), streamType, streamTypeAlias); } // If either the client forces allowing ringer modes for this adjustment, // or stream is used for UI sonification if (((flags AudioManager.FLAG_ALLOW_RINGER_MODES) ! 0) || (isUiSoundsStreamType(streamTypeAlias))) { int ringerMode getRingerModeInternal(); // do not vibrate if already in vibrate mode if (ringerMode AudioManager.RINGER_MODE_VIBRATE) { flags ~AudioManager.FLAG_VIBRATE; } // Check if the ringer mode handles this adjustment. If it does we dont // need to adjust the volume further. final int result checkForRingerModeChange(aliasIndex, direction, step, streamState.mIsMuted, callingPackage, flags); 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) { flags | AudioManager.FLAG_SHOW_SILENT_HINT; } // If suppressing a volume down adjustment in vibrate mode, display the UI hint if ((result AudioManager.FLAG_SHOW_VIBRATE_HINT) ! 0) { flags | AudioManager.FLAG_SHOW_VIBRATE_HINT; } } else if (isStreamMutedByRingerOrZenMode(streamTypeAlias) streamState.mIsMuted) { // if the stream is currently muted streams by ringer/zen mode // then it cannot be unmuted (without FLAG_ALLOW_RINGER_MODES) with an unmute or raise if (direction AudioManager.ADJUST_TOGGLE_MUTE || direction AudioManager.ADJUST_UNMUTE || direction AudioManager.ADJUST_RAISE) { adjustVolume false; } } // If the ringer mode or zen is muting the stream, do not change stream unless // itll cause us to exit dnd if (!volumeAdjustmentAllowedByDnd(streamTypeAlias, flags)) { adjustVolume false; } int oldIndex getVssForStreamOrDefault(streamType).getIndex(device); // Check if the volume adjustment should be handled by an absolute volume controller instead if (isAbsoluteVolumeDevice(device) (flags AudioManager.FLAG_ABSOLUTE_VOLUME) 0) { final AbsoluteVolumeDeviceInfo info getAbsoluteVolumeDeviceInfo(device); if (info.mHandlesVolumeAdjustment) { dispatchAbsoluteVolumeAdjusted(streamType, info, oldIndex, direction, keyEventMode); return; } } if (adjustVolume (direction ! AudioManager.ADJUST_SAME) (keyEventMode ! AudioDeviceVolumeManager.ADJUST_MODE_END)) { mAudioHandler.removeMessages(MSG_UNMUTE_STREAM_ON_SINGLE_VOL_DEVICE); boolean isInstallCtsView SystemProperties.getBoolean(persist.skg.isinstall.cts.view, false); if (!isInstallCtsView) { if (isMuteAdjust) { boolean state; if (direction AudioManager.ADJUST_TOGGLE_MUTE) { state !streamState.mIsMuted; } else { state direction AudioManager.ADJUST_MUTE; } muteAliasStreams(streamTypeAlias, state); } else if ((direction AudioManager.ADJUST_RAISE) mSoundDoseHelper.raiseVolumeDisplaySafeMediaVolume(streamTypeAlias, aliasIndex step, device, flags)) { Log.e(TAG, adjustStreamVolume() safe volume index oldIndex); } else if ((streamState.adjustIndex(direction * step, device, caller, hasModifyAudioSettings) || streamState.mIsMuted)) { // Post message to set system volume (it in turn will post a // message to persist). if (streamState.mIsMuted) { // Unmute the stream if it was previously muted if (direction AudioManager.ADJUST_RAISE) { // unmute immediately for volume up muteAliasStreams(streamTypeAlias, false); } else if (direction AudioManager.ADJUST_LOWER) { if (mIsSingleVolume) { sendMsg(mAudioHandler, MSG_UNMUTE_STREAM_ON_SINGLE_VOL_DEVICE, SENDMSG_QUEUE, streamTypeAlias, flags, null, UNMUTE_STREAM_DELAY); } } } sendMsg(mAudioHandler, MSG_SET_DEVICE_VOLUME, SENDMSG_QUEUE, device, 0, streamState, 0); } } else { if (isMuteAdjust !mFullVolumeDevices.contains(device)) { boolean state; if (direction AudioManager.ADJUST_TOGGLE_MUTE) { state !streamState.mIsMuted; } else { state direction AudioManager.ADJUST_MUTE; } muteAliasStreams(streamTypeAlias, state); } else if ((direction AudioManager.ADJUST_RAISE) mSoundDoseHelper.raiseVolumeDisplaySafeMediaVolume(streamTypeAlias, aliasIndex step, device, flags)) { Log.e(TAG, adjustStreamVolume() safe volume index oldIndex); } else if (!isFullVolumeDevice(device) (streamState.adjustIndex(direction * step, device, caller, hasModifyAudioSettings) || streamState.mIsMuted)) { // Post message to set system volume (it in turn will post a // message to persist). if (streamState.mIsMuted) { // Unmute the stream if it was previously muted if (direction AudioManager.ADJUST_RAISE) { // unmute immediately for volume up muteAliasStreams(streamTypeAlias, false); } else if (direction AudioManager.ADJUST_LOWER) { if (mIsSingleVolume) { sendMsg(mAudioHandler, MSG_UNMUTE_STREAM_ON_SINGLE_VOL_DEVICE, SENDMSG_QUEUE, streamTypeAlias, flags, null, UNMUTE_STREAM_DELAY); } } } sendMsg(mAudioHandler, MSG_SET_DEVICE_VOLUME, SENDMSG_QUEUE, device, 0, streamState, 0); } } int newIndex getVssForStreamOrDefault(streamType).getIndex(device); // Check if volume update should be send to AVRCP if (streamTypeAlias getBluetoothContextualVolumeStream() AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device) (flags AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) 0) { if (DEBUG_VOL) { Log.d(TAG, adjustStreamVolume: postSetAvrcpAbsoluteVolumeIndex index newIndex stream streamType); } mDeviceBroker.postSetAvrcpAbsoluteVolumeIndex(newIndex / 10); } else if (isAbsoluteVolumeDevice(device) (flags AudioManager.FLAG_ABSOLUTE_VOLUME) 0) { final AbsoluteVolumeDeviceInfo info getAbsoluteVolumeDeviceInfo(device); dispatchAbsoluteVolumeChanged(streamType, info, newIndex); } if (AudioSystem.isLeAudioDeviceType(device) streamType getBluetoothContextualVolumeStream() (flags AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) 0) { if (DEBUG_VOL) { Log.d(TAG, adjustStreamVolume postSetLeAudioVolumeIndex index newIndex stream streamType); } mDeviceBroker.postSetLeAudioVolumeIndex(newIndex, getVssForStreamOrDefault(streamType).getMaxIndex(), streamType); } // Check if volume update should be send to Hearing Aid. // Only modify the hearing aid attenuation when the stream to modify matches // the one expected by the hearing aid. if (device AudioSystem.DEVICE_OUT_HEARING_AID streamType getBluetoothContextualVolumeStream()) { if (DEBUG_VOL) { Log.d(TAG, adjustStreamVolume postSetHearingAidVolumeIndex index newIndex stream streamType); } int haIndex; final VolumeStreamState vss getVssForStreamOrDefault(streamType); synchronized (mVolumeStateLock) { haIndex (int) (vss.getMinIndex() (newIndex - vss.getMinIndex()) / vss.getIndexStepFactor()); } mDeviceBroker.postSetHearingAidVolumeIndex(haIndex, streamType); } } final int newIndex getVssForStreamOrDefault(streamType).getIndex(device); if (adjustVolume) { synchronized (mHdmiClientLock) { if (mHdmiManager ! null) { // At most one of mHdmiPlaybackClient and mHdmiTvClient should be non-null HdmiClient hdmiClient mHdmiPlaybackClient; if (mHdmiTvClient ! null) { hdmiClient mHdmiTvClient; } boolean playbackDeviceConditions mHdmiPlaybackClient ! null isFullVolumeDevice(device); boolean tvConditions mHdmiTvClient ! null mHdmiSystemAudioSupported !isAbsoluteVolumeDevice(device) !isA2dpAbsoluteVolumeDevice(device); if ((playbackDeviceConditions || tvConditions) mHdmiCecVolumeControlEnabled streamTypeAlias AudioSystem.STREAM_MUSIC) { int keyCode KeyEvent.KEYCODE_UNKNOWN; switch (direction) { case AudioManager.ADJUST_RAISE: keyCode KeyEvent.KEYCODE_VOLUME_UP; break; case AudioManager.ADJUST_LOWER: keyCode KeyEvent.KEYCODE_VOLUME_DOWN; break; case AudioManager.ADJUST_TOGGLE_MUTE: case AudioManager.ADJUST_MUTE: if(streamState.mIsMuted) { Log.d(TAG,stream is already mute, not send cec command); break; } case AudioManager.ADJUST_UNMUTE: if(!streamState.mIsMuted) { Log.d(TAG,stream is already unmute, not send cec command); break; } // Many CEC devices only support toggle mute. Therefore, we send the // same keycode for all three mute options. keyCode KeyEvent.KEYCODE_VOLUME_MUTE; break; default: break; } if (keyCode ! KeyEvent.KEYCODE_UNKNOWN) { final long ident Binder.clearCallingIdentity(); try { switch (keyEventMode) { case AudioDeviceVolumeManager.ADJUST_MODE_NORMAL: hdmiClient.sendVolumeKeyEvent(keyCode, true); hdmiClient.sendVolumeKeyEvent(keyCode, false); break; case AudioDeviceVolumeManager.ADJUST_MODE_START: hdmiClient.sendVolumeKeyEvent(keyCode, true); break; case AudioDeviceVolumeManager.ADJUST_MODE_END: hdmiClient.sendVolumeKeyEvent(keyCode, false); break; default: Log.e(TAG, Invalid keyEventMode keyEventMode); } } finally { Binder.restoreCallingIdentity(ident); } } } if (streamTypeAlias AudioSystem.STREAM_MUSIC (oldIndex ! newIndex || isMuteAdjust)) { maybeSendSystemAudioStatusCommand(isMuteAdjust); } } } } sendVolumeUpdate(streamType, oldIndex, newIndex, flags, device); }这里代码较多但是在最后可以看到HdmiClient.sendVolumeKeyEvent的操作这与我们的HdmiCec有些相似先往下分析看看。//base/core/java/android/hardware/hdmi/HdmiClient.java /* package */ HdmiClient(IHdmiControlService service) { mService service; } /** * Sends a volume key event to the primary audio receiver in the system. This method should only * be called when the volume key is not handled by the local device. HDMI framework handles the * logic of finding the address of the receiver. * * param keyCode key code to send. Defined in {link android.view.KeyEvent}. * param isPressed true if this is key press event * * hide */ public void sendVolumeKeyEvent(int keyCode, boolean isPressed) { try { mService.sendVolumeKeyEvent(getDeviceType(), keyCode, isPressed); } catch (RemoteException e) { Log.e(TAG, sendVolumeKeyEvent threw exception , e); throw e.rethrowFromSystemServer(); } }//base/service/core/java/com/android/server/hdmi/HdmiControlService.java Override public void sendVolumeKeyEvent( final int deviceType, final int keyCode, final boolean isPressed) { initBinderCall(); runOnServiceThread(new Runnable() { Override public void run() { if (mCecController null) { Slog.w(TAG, CEC controller not available to send volume key event.); return; } HdmiCecLocalDevice localDevice mHdmiCecNetwork.getLocalDevice(deviceType); if (localDevice null) { Slog.w(TAG, Local device deviceType not available to send volume key event.); return; } localDevice.sendVolumeKeyEvent(keyCode, isPressed); } }); }//base/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java /** * Send a volume key event to other CEC device. The logical address of target device will be * given by {link #findAudioReceiverAddress()}. * * param keyCode key code defined in {link android.view.KeyEvent} * param isPressed {code true} for key down event * see #findAudioReceiverAddress() */ ServiceThreadOnly protected void sendVolumeKeyEvent(int keyCode, boolean isPressed) { assertRunOnServiceThread(); if (mService.getHdmiCecVolumeControl() HdmiControlManager.VOLUME_CONTROL_DISABLED) { return; } if (!HdmiCecKeycode.isVolumeKeycode(keyCode)) { Slog.w(TAG, Not a volume key: keyCode); return; } ListSendKeyAction action getActions(SendKeyAction.class); int logicalAddress findAudioReceiverAddress(); if (logicalAddress Constants.ADDR_INVALID || mService.getAllCecLocalDevices().stream().anyMatch( device - device.getDeviceInfo().getLogicalAddress() logicalAddress)) { // Dont send key event to invalid device or itself. Slog.w( TAG, Discard volume key event: keyCode , pressed: isPressed , receiverAddr logicalAddress); } else if (!action.isEmpty()) { action.get(0).processKeyEvent(keyCode, isPressed); } else if (isPressed) { addAndStartAction(new SendKeyAction(this, logicalAddress, keyCode)); } } ServiceThreadOnly void addAndStartAction(final HdmiCecFeatureAction action) { assertRunOnServiceThread(); mActions.add(action); if (mService.isPowerStandby() || !mService.isAddressAllocated()) { if (action.getClass() ResendCecCommandAction.class) { Slog.i(TAG, Not ready to start ResendCecCommandAction. This action is cancelled.); removeAction(action); return; } Slog.i(TAG, Not ready to start action. Queued for deferred start: action); return; } action.start(); }//base/services/com/java/com/android/server/hdmi/SendKeyAction.java Override public boolean start() { sendKeyDown(mLastKeycode); mLastSendKeyTime getCurrentTime(); // finish action for non-repeatable key. if (!HdmiCecKeycode.isRepeatableKey(mLastKeycode)) { sendKeyUp(); finish(); return true; } mState STATE_CHECKING_LONGPRESS; addTimer(mState, AWAIT_LONGPRESS_MS); return true; } private void sendKeyDown(int keycode) { byte[] cecKeycodeAndParams HdmiCecKeycode.androidKeyToCecKey(keycode); if (cecKeycodeAndParams null) { return; } // Devices that are not directly connected with audio system device cant detect if the // audio system device is still plugged in. Framework checks if the volume key forwarding is // successful or not every time to make sure the System Audio Mode status is still updated. if (mTargetAddress Constants.ADDR_AUDIO_SYSTEM localDevice().getDeviceInfo().getLogicalAddress() ! Constants.ADDR_TV) { sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(getSourceAddress(), mTargetAddress, cecKeycodeAndParams), new SendMessageCallback() { Override public void onSendCompleted(int error) { // Disable System Audio Mode, if the AVR doesnt acknowledge // a User Control Pressed message. if (error SendMessageResult.NACK) { HdmiLogger.debug( AVR did not acknowledge User Control Pressed); localDevice().mService.setSystemAudioActivated(false); } } }); } else { sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(getSourceAddress(), mTargetAddress, cecKeycodeAndParams)); } }//base/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java private final HdmiControlService mService; protected final void sendCommand(HdmiCecMessage cmd) { mService.sendCecCommand(cmd); }//base/services/core/java/com/android/server/hdmi/HdmiControlService.java ServiceThreadOnly void sendCecCommand(HdmiCecMessage command) { sendCecCommand(command, null); } ServiceThreadOnly void sendCecCommand(HdmiCecMessage command, Nullable SendMessageCallback callback) { switch (command.getOpcode()) { case Constants.MESSAGE_ACTIVE_SOURCE: case Constants.MESSAGE_IMAGE_VIEW_ON: case Constants.MESSAGE_INACTIVE_SOURCE: case Constants.MESSAGE_ROUTING_CHANGE: case Constants.MESSAGE_SET_STREAM_PATH: case Constants.MESSAGE_TEXT_VIEW_ON: // RequestActiveSourceAction is started after the TV finished logical address // allocation. This action is used by the TV to get the active source from the CEC // network. If the TV sent a source changing CEC message, this action does not have // to continue anymore. if (isTvDeviceEnabled()) { tv().removeAction(RequestActiveSourceAction.class); } sendCecCommandWithRetries(command, callback); break; default: sendCecCommandWithoutRetries(command, callback); } } /** * Create a {link ResendCecCommandAction} that will retry sending the CEC message if it fails. * param command command to be sent on the CEC bus. * param callback callback for handling the result of sending the command. */ ServiceThreadOnly private void sendCecCommandWithRetries(HdmiCecMessage command, Nullable SendMessageCallback callback) { assertRunOnServiceThread(); ListHdmiCecLocalDevice devices getAllCecLocalDevices(); if (devices.isEmpty()) { return; } HdmiCecLocalDevice localDevice devices.get(0); if (localDevice ! null) { sendCecCommandWithoutRetries(command, new SendMessageCallback() { Override public void onSendCompleted(int result) { if (result ! SendMessageResult.SUCCESS) { localDevice.addAndStartAction(new ResendCecCommandAction(localDevice, command, callback)); } else if (callback ! null) { callback.onSendCompleted(result); } } }); } } /** * Transmit a CEC command to CEC bus. * * param command CEC command to send out * param callback interface used to the result of send command */ ServiceThreadOnly void sendCecCommandWithoutRetries(HdmiCecMessage command, Nullable SendMessageCallback callback) { assertRunOnServiceThread(); if (command.getValidationResult() HdmiCecMessageValidator.OK verifyPhysicalAddresses(command)) { mCecController.sendCommand(command, callback); } else { HdmiLogger.error(Invalid message type: command); if (callback ! null) { callback.onSendCompleted(SendMessageResult.FAIL); } } }最终调用到mCecController.sendCommand(command, callback);也就是HdmiCecController中说明是adjustStreamVolume中响应了mute的请求查看我们修改的系统逻辑原来在系统UI中为了在按下音量加减时能够解除静音再次调用了解除静音的请求而这里都识别成同一个keycode也就导致了问题。case AudioManager.ADJUST_TOGGLE_MUTE: case AudioManager.ADJUST_MUTE: case AudioManager.ADJUST_UNMUTE: // Many CEC devices only support toggle mute. Therefore, we send the // same keycode for all three mute options. keyCode KeyEvent.KEYCODE_VOLUME_MUTE;修改的话可以判断当前调整的流是否与目标状态一致是则不需要再发送keycode直接跳过即可。case AudioManager.ADJUST_MUTE: if(streamState.mIsMuted) { Log.d(TAG,stream is already mute, not send cec command); break; } case AudioManager.ADJUST_UNMUTE: if(!streamState.mIsMuted) { Log.d(TAG,stream is already unmute, not send cec command); break; } // Many CEC devices only support toggle mute. Therefore, we send the // same keycode for all three mute options. keyCode KeyEvent.KEYCODE_VOLUME_MUTE; break;
返回列表