Add sendVolumeKeyEvent API to handle forwarding volume key separately.
Note that we handle volume key the same way as other keys before when forwarding them to other cec devices. But the destination device of volume key might be different from other function keys. We might need to take the System Audio Control logic into this forwarding proccess to find the proper audio receiver address. Test: make -j44 Bug: 123369653 Change-Id: I6c9dba2b333e7eaa5137a8d2f5bfed506ae8554b (cherry picked from commit 5db0138297836baf570f354c0b2ecb988d30936b)
This commit is contained in:
@@ -55,6 +55,26 @@ public abstract class HdmiClient {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* TODO(b/110094868): unhide for Q
|
||||
*/
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends vendor-specific command.
|
||||
*
|
||||
|
||||
@@ -47,6 +47,7 @@ interface IHdmiControlService {
|
||||
void deviceSelect(int deviceId, IHdmiControlCallback callback);
|
||||
void portSelect(int portId, IHdmiControlCallback callback);
|
||||
void sendKeyEvent(int deviceType, int keyCode, boolean isPressed);
|
||||
void sendVolumeKeyEvent(int deviceType, int keyCode, boolean isPressed);
|
||||
List<HdmiPortInfo> getPortInfo();
|
||||
boolean canChangeSystemAudioMode();
|
||||
boolean getSystemAudioMode();
|
||||
|
||||
@@ -180,6 +180,11 @@ public class HdmiAudioSystemClientTest {
|
||||
public void sendKeyEvent(final int deviceType, final int keyCode, final boolean isPressed) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendVolumeKeyEvent(
|
||||
final int deviceType, final int keyCode, final boolean isPressed) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void oneTouchPlay(final IHdmiControlCallback callback) {
|
||||
}
|
||||
|
||||
@@ -449,6 +449,20 @@ final class HdmiCecKeycode {
|
||||
return HdmiCecKeycode.androidKeyToCecKey(androidKeycode) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if given Android keycode is volume control related,
|
||||
* otherwise {@code false}.
|
||||
*/
|
||||
static boolean isVolumeKeycode(int androidKeycode) {
|
||||
int cecKeyCode = HdmiCecKeycode.androidKeyToCecKey(androidKeycode)[0];
|
||||
return isSupportedKeycode(androidKeycode)
|
||||
&& (cecKeyCode == CEC_KEYCODE_VOLUME_UP
|
||||
|| cecKeyCode == CEC_KEYCODE_VOLUME_DOWN
|
||||
|| cecKeyCode == CEC_KEYCODE_MUTE
|
||||
|| cecKeyCode == CEC_KEYCODE_MUTE_FUNCTION
|
||||
|| cecKeyCode == CEC_KEYCODE_RESTORE_VOLUME_FUNCTION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns CEC keycode to control audio mute status.
|
||||
*
|
||||
|
||||
@@ -1015,6 +1015,40 @@ abstract class HdmiCecLocalDevice {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 (!HdmiCecKeycode.isVolumeKeycode(keyCode)) {
|
||||
Slog.w(TAG, "Not a volume key: " + keyCode);
|
||||
return;
|
||||
}
|
||||
List<SendKeyAction> action = getActions(SendKeyAction.class);
|
||||
int logicalAddress = findAudioReceiverAddress();
|
||||
if (logicalAddress == Constants.ADDR_INVALID || logicalAddress == mAddress) {
|
||||
// Don't 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));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the logical address of the device which will receive key events via {@link
|
||||
* #sendKeyEvent}.
|
||||
@@ -1026,6 +1060,17 @@ abstract class HdmiCecLocalDevice {
|
||||
return Constants.ADDR_INVALID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the logical address of the audio receiver device which will receive volume key events
|
||||
* via {@link#sendVolumeKeyEvent}.
|
||||
*
|
||||
* @see #sendVolumeKeyEvent(int, boolean)
|
||||
*/
|
||||
protected int findAudioReceiverAddress() {
|
||||
Slog.w(TAG, "findAudioReceiverAddress is not implemented");
|
||||
return Constants.ADDR_INVALID;
|
||||
}
|
||||
|
||||
@ServiceThreadOnly
|
||||
void invokeCallback(IHdmiControlCallback callback, int result) {
|
||||
assertRunOnServiceThread();
|
||||
|
||||
@@ -1526,7 +1526,7 @@ public class HdmiControlService extends SystemService {
|
||||
if (mCecController != null) {
|
||||
HdmiCecLocalDevice localDevice = mCecController.getLocalDevice(deviceType);
|
||||
if (localDevice == null) {
|
||||
Slog.w(TAG, "Local device not available");
|
||||
Slog.w(TAG, "Local device not available to send key event.");
|
||||
return;
|
||||
}
|
||||
localDevice.sendKeyEvent(keyCode, isPressed);
|
||||
@@ -1535,6 +1535,28 @@ public class HdmiControlService extends SystemService {
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendVolumeKeyEvent(
|
||||
final int deviceType, final int keyCode, final boolean isPressed) {
|
||||
enforceAccessPermission();
|
||||
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 = mCecController.getLocalDevice(deviceType);
|
||||
if (localDevice == null) {
|
||||
Slog.w(TAG, "Local device " + deviceType
|
||||
+ " not available to send volume key event.");
|
||||
return;
|
||||
}
|
||||
localDevice.sendVolumeKeyEvent(keyCode, isPressed);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void oneTouchPlay(final IHdmiControlCallback callback) {
|
||||
enforceAccessPermission();
|
||||
|
||||
Reference in New Issue
Block a user