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:
Amy
2019-01-30 11:27:49 -08:00
committed by Shubang Lu
parent 131cbcc36b
commit bd8b4fa1ae
6 changed files with 108 additions and 1 deletions

View File

@@ -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.
*

View File

@@ -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();

View File

@@ -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) {
}

View File

@@ -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.
*

View File

@@ -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();

View File

@@ -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();