summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/hdmi/Constants.java22
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java28
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java11
-rw-r--r--services/core/java/com/android/server/hdmi/RequestShortAudioDescriptorAction.java222
4 files changed, 283 insertions, 0 deletions
diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java
index a2a55e532ef4..913314b2d192 100644
--- a/services/core/java/com/android/server/hdmi/Constants.java
+++ b/services/core/java/com/android/server/hdmi/Constants.java
@@ -179,6 +179,25 @@ final class Constants {
static final int MENU_STATE_ACTIVATED = 0;
static final int MENU_STATE_DEACTIVATED = 1;
+ // Audio Format Codes
+ // Refer to CEA Standard (CEA-861-D), Table 37 Audio Format Codes.
+ static final int MSAPI_CODEC_NONE = 0x0;
+ static final int MSAPI_CODEC_LPCM = 0x1; // Support LPCM
+ static final int MSAPI_CODEC_DD = 0x2; // Support DD
+ static final int MSAPI_CODEC_MPEG1 = 0x3; // Support MPEG1
+ static final int MSAPI_CODEC_MP3 = 0x4; // Support MP3
+ static final int MSAPI_CODEC_MPEG2 = 0x5; // Support MPEG2
+ static final int MSAPI_CODEC_AAC = 0x6; // Support AAC
+ static final int MSAPI_CODEC_DTS = 0x7; // Support DTS
+ static final int MSAPI_CODEC_ATRAC = 0x8; // Support ATRAC
+ static final int MSAPI_CODEC_ONEBITAUDIO = 0x9;// Support One-Bit Audio
+ static final int MSAPI_CODEC_DDP = 0xA; // Support DDP
+ static final int MSAPI_CODEC_DTSHD = 0xB; // Support DTSHD
+ static final int MSAPI_CODEC_TRUEHD = 0xC; // Support MLP/TRUE-HD
+ static final int MSAPI_CODEC_DST = 0xD; // Support DST
+ static final int MSAPI_CODEC_WMAPRO = 0xE; // Support WMA-Pro
+ static final int MSAPI_CODEC_MAX = 0xF;
+
// Bit mask used to get the routing path of the top level device.
// When &'d with the path 1.2.2.0 (0x1220), for instance, gives 1.0.0.0.
static final int ROUTING_PATH_TOP_MASK = 0xF000;
@@ -227,6 +246,9 @@ final class Constants {
// Definitions used for setOption(). These should be in sync with the definition
// in hardware/libhardware/include/hardware/mhl.h.
+ // If set to enabled, system support multichannels.
+ static final int OPTION_CEC_SUPPORT_MULTICHANNELS = 6;
+
// If set to disabled, TV does not switch ports when mobile device is connected.
static final int OPTION_MHL_INPUT_SWITCHING = 101;
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 1e09383db56d..46c2e7bceee2 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -50,6 +50,7 @@ import android.util.SparseBooleanArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.hdmi.DeviceDiscoveryAction.DeviceDiscoveryCallback;
+import com.android.server.hdmi.RequestShortAudioDescriptorAction.RequestSADCallback;
import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
import java.io.UnsupportedEncodingException;
@@ -848,6 +849,24 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
synchronized (mLock) {
if (mSystemAudioActivated != on) {
mSystemAudioActivated = on;
+ if (getAvrDeviceInfo() != null) {
+ RequestShortAudioDescriptorAction action =
+ new RequestShortAudioDescriptorAction(this,
+ getAvrDeviceInfo().getLogicalAddress(),
+ getAvrDeviceInfo().getPortId(), on,
+ new RequestSADCallback(){
+ @Override
+ public void updateSAD(String keyValuePairs,
+ boolean supportMultiChannels) {
+ mService.getAudioManager().setParameters(keyValuePairs);
+ mService.setCecOption(
+ Constants.OPTION_CEC_SUPPORT_MULTICHANNELS,
+ supportMultiChannels ?
+ Constants.ENABLED : Constants.DISABLED);
+ }
+ });
+ addAndStartAction(action);
+ }
mService.announceSystemAudioModeChange(on);
}
startArcAction(on);
@@ -1462,6 +1481,13 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
@ServiceThreadOnly
final void removeCecDevice(int address) {
assertRunOnServiceThread();
+
+ HdmiDeviceInfo avr = getAvrDeviceInfo();
+ if ((avr != null) && (address == avr.getLogicalAddress())) {
+ removeAction(RequestShortAudioDescriptorAction.class);
+ RequestShortAudioDescriptorAction.removeAudioFormat();
+ }
+
HdmiDeviceInfo info = removeDeviceInfo(HdmiDeviceInfo.idForCecDevice(address));
mCecMessageCache.flushMessagesFrom(address);
@@ -1658,6 +1684,8 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
return;
}
+ removeAction(RequestShortAudioDescriptorAction.class);
+ RequestShortAudioDescriptorAction.removeAudioFormat();
// Seq #44.
removeAction(RequestArcInitiationAction.class);
if (!hasAction(RequestArcTerminationAction.class) && isArcEstablished()) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
index 9a51e3c4e234..0c096d24f7d6 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
@@ -504,6 +504,17 @@ public class HdmiCecMessageBuilder {
static HdmiCecMessage buildSetDigitalTimer(int src, int dest, byte[] params) {
return buildCommand(src, dest, Constants.MESSAGE_SET_DIGITAL_TIMER, params);
}
+ /**
+ * Build <Request Short Audio Descriptor> command.
+ *
+ * @param src source address of command
+ * @param dest destination address of command
+ * @param params byte array of audio format ID and code
+ * @return newly created {@link HdmiCecMessage}
+ */
+ static HdmiCecMessage buildRequestShortAudioDescriptor(int src, int dest, byte[] params) {
+ return buildCommand(src, dest, Constants.MESSAGE_REQUEST_SHORT_AUDIO_DESCRIPTOR, params);
+ }
/**
* Build <Set Analogue Timer> command.
diff --git a/services/core/java/com/android/server/hdmi/RequestShortAudioDescriptorAction.java b/services/core/java/com/android/server/hdmi/RequestShortAudioDescriptorAction.java
new file mode 100644
index 000000000000..3f2c9592b950
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/RequestShortAudioDescriptorAction.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+import android.content.Context;
+import android.hardware.hdmi.HdmiDeviceInfo;
+import android.provider.Settings;
+import android.util.Slog;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Arrays;
+
+
+/**
+ * Feature action that handles Request Short Audio Description.
+ * To detect AVR device supported audio codec.
+ */
+final class RequestShortAudioDescriptorAction extends HdmiCecFeatureAction {
+ private static final String TAG = "RequestShortAudioDescriptor";
+
+ /**
+ * Interface used to update Short Audio Descriptor.
+ */
+ interface RequestSADCallback {
+ /**
+ * Called when system audio mode is set.
+ *
+ * @param keyValuePairs a set of SADs needs to pass to audio HAL. Format will be like as
+ * set_ARC_format=[Parameter Length, ARC port] [SADs(3 bytes for 1 SAD)]
+ * If Parameter Length is 0, restore EDID.
+ * @param supportMultiChannels indicates if support multi-channels
+ */
+ void updateSAD(String keyValuePairs, boolean supportMultiChannels);
+ }
+
+ // State in which the action sent <Request Short Audio Descriptor> and
+ // is waiting for time out. If it receives <Feature Abort> within timeout.
+ private static final int STATE_WAITING_TIMEOUT = 1;
+
+ private final boolean mEnabled;
+ private final int mAvrAddress;
+ private final int mAvrPort;
+ private static byte[] mParamsBackup;
+ private final int SAD_LEN_MAX = 12;
+ private final int SAD_LEN = 3; //length of short audio descriptor is 3 bytes
+ private final RequestSADCallback mCallback;
+
+ /**
+ * Constructor
+ *
+ * @param source {@link HdmiCecLocalDevice} instance
+ * @param avrAddress logical address of AVR
+ * @param avrPort indicates which port is connected to AVR
+ * @param systemAudioModeEnabled if not enabled, reset short audio descriptors
+ * @throw IllegalArugmentException if device type of sourceAddress and avrAddress
+ * is invalid
+ */
+ RequestShortAudioDescriptorAction(HdmiCecLocalDevice source, int avrAddress,
+ int avrPort, boolean systemAudioModeEnabled, RequestSADCallback callback) {
+ super(source);
+ HdmiUtils.verifyAddressType(getSourceAddress(), HdmiDeviceInfo.DEVICE_TV);
+ HdmiUtils.verifyAddressType(avrAddress, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
+ mAvrAddress = avrAddress;
+ mEnabled = systemAudioModeEnabled;
+ mAvrPort = avrPort;
+ mCallback = Preconditions.checkNotNull(callback);
+ }
+
+ @Override
+ boolean start() {
+ if (mEnabled) {
+ mState = STATE_WAITING_TIMEOUT;
+ addTimer(mState, HdmiConfig.TIMEOUT_MS);
+ if (mParamsBackup != null) {
+ HdmiLogger.debug("Set old audio format");
+ setAudioFormat();
+ } else {
+ HdmiLogger.debug("No old audio format. Send a command to reqeust.");
+ sendRequestShortAudioDescriptor();
+ }
+ } else {
+ resetShortAudioDescriptor();
+ finish();
+ }
+ return true;
+ }
+
+ private void sendRequestShortAudioDescriptor() {
+ byte[] params = new byte[4];
+ params[0] = (byte) Constants.MSAPI_CODEC_DD;
+ params[1] = (byte) Constants.MSAPI_CODEC_AAC;
+ params[2] = (byte) Constants.MSAPI_CODEC_DTS;
+ params[3] = (byte) Constants.MSAPI_CODEC_DDP;
+
+ HdmiCecMessage command =
+ HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(getSourceAddress(),
+ mAvrAddress, params);
+ sendCommand(command, new HdmiControlService.SendMessageCallback() {
+ @Override
+ public void onSendCompleted(int error) {
+ switch (error) {
+ case Constants.SEND_RESULT_SUCCESS:
+ case Constants.SEND_RESULT_BUSY:
+ case Constants.SEND_RESULT_FAILURE:
+ //Ignores it silently.
+ break;
+ case Constants.SEND_RESULT_NAK:
+ HdmiLogger.debug("Failed to send <Request Short Audio Descriptor>.");
+ finish();
+ break;
+ }
+ }
+ });
+ }
+
+ private void resetShortAudioDescriptor() {
+ String audioParameter = "set_ARC_format=";
+ String keyValuePairs;
+ byte[] buffer = new byte[2];
+ buffer[0] = (byte) 0x00;
+ buffer[1] = (byte) mAvrPort;
+ keyValuePairs = audioParameter + Arrays.toString(buffer);
+ mCallback.updateSAD(keyValuePairs, false);
+ }
+
+ public static void removeAudioFormat() {
+ HdmiLogger.debug("Remove audio format.");
+ mParamsBackup = null;
+ }
+
+ private boolean isMultiChannelsSupported() {
+ byte codec = Constants.MSAPI_CODEC_NONE;
+ byte channels = 0;
+ for (int index = 0; index < mParamsBackup.length; index += SAD_LEN) {
+ // bit 6~3: Audio Format Code
+ codec = (byte) ((mParamsBackup[index] & 0x78) >> 3); //enAudioFormatCode
+ // bit 2~0: Max number of channels -1
+ channels = (byte) (mParamsBackup[index] & 0x07);
+ if ((codec == Constants.MSAPI_CODEC_DDP) || (codec == Constants.MSAPI_CODEC_DD)) {
+ if (channels >= 5) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private void setAudioFormat() {
+ byte[] buffer = new byte[2];
+ String audioParameter = "set_ARC_format=";
+ String keyValuePairs;
+
+ buffer[0] = (byte) (mParamsBackup.length);
+ buffer[1] = (byte) (mAvrPort);
+ keyValuePairs = audioParameter + Arrays.toString(buffer);
+ keyValuePairs += Arrays.toString(mParamsBackup);
+ HdmiLogger.debug("keyValuePairs:" + keyValuePairs);
+ mCallback.updateSAD(keyValuePairs, isMultiChannelsSupported());
+ finish();
+ }
+
+ @Override
+ boolean processCommand(HdmiCecMessage cmd) {
+ if (mState != STATE_WAITING_TIMEOUT) {
+ return false;
+ }
+
+ int opcode = cmd.getOpcode();
+ byte[] params = cmd.getParams();
+ if (opcode == Constants.MESSAGE_FEATURE_ABORT) {
+ int originalOpcode = cmd.getParams()[0] & 0xFF;
+ if (originalOpcode == Constants.MESSAGE_REQUEST_SHORT_AUDIO_DESCRIPTOR) {
+ HdmiLogger.debug("Feature aborted for <Request Short Audio Descriptor>");
+ finish();
+ return true;
+ }
+ } else if (opcode == Constants.MESSAGE_REPORT_SHORT_AUDIO_DESCRIPTOR) {
+ HdmiLogger.debug("ProcessCommand: <Report Short Audio Descriptor>");
+ HdmiLogger.debug("length:" + params.length);
+ if ((params.length == 0) || (params.length > SAD_LEN_MAX)) {
+ finish();
+ return false;
+ }
+ if ((params[0] & 0xFF) == Constants.MSAPI_CODEC_NONE) {
+ resetShortAudioDescriptor();
+ finish();
+ return true;
+ }
+
+ mParamsBackup = new byte[params.length];
+ mParamsBackup = Arrays.copyOf(params, params.length);
+ setAudioFormat();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ void handleTimerEvent(int state) {
+ if (mState != state || mState != STATE_WAITING_TIMEOUT) {
+ return;
+ }
+ // Expire timeout for <Feature Abort>.
+ HdmiLogger.debug("[T]RequestShortAudioDescriptorAction.");
+ finish();
+ }
+}