summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/hardware/hdmi/HdmiClient.java21
-rw-r--r--core/java/android/hardware/hdmi/HdmiControlManager.java37
-rw-r--r--core/java/android/hardware/hdmi/HdmiTvClient.java25
-rw-r--r--media/java/android/media/tv/ITvInputHardware.aidl4
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java6
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java7
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java5
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java48
-rw-r--r--services/core/java/com/android/server/tv/TvInputHardwareManager.java62
9 files changed, 166 insertions, 49 deletions
diff --git a/core/java/android/hardware/hdmi/HdmiClient.java b/core/java/android/hardware/hdmi/HdmiClient.java
index c43e21acc344..5d26a57f289d 100644
--- a/core/java/android/hardware/hdmi/HdmiClient.java
+++ b/core/java/android/hardware/hdmi/HdmiClient.java
@@ -1,5 +1,6 @@
package android.hardware.hdmi;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.hardware.hdmi.HdmiControlManager.VendorCommandListener;
import android.hardware.hdmi.IHdmiVendorCommandListener;
@@ -25,6 +26,21 @@ public abstract class HdmiClient {
}
/**
+ * Returns the active source information.
+ *
+ * @return {@link HdmiCecDeviceInfo} object that describes the active source
+ * or active routing path
+ */
+ public HdmiCecDeviceInfo getActiveSource() {
+ try {
+ return mService.getActiveSource();
+ } catch (RemoteException e) {
+ Log.e(TAG, "getActiveSource threw exception ", e);
+ }
+ return null;
+ }
+
+ /**
* Send a key event to other logical device.
*
* @param keyCode key code to send. Defined in {@link android.view.KeyEvent}.
@@ -60,7 +76,10 @@ public abstract class HdmiClient {
*
* @param listener listener object
*/
- public void addVendorCommandListener(VendorCommandListener listener) {
+ public void addVendorCommandListener(@NonNull VendorCommandListener listener) {
+ if (listener == null) {
+ throw new IllegalArgumentException("listener cannot be null");
+ }
try {
mService.addVendorCommandListener(getListenerWrapper(listener), getDeviceType());
} catch (RemoteException e) {
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index a4a3ac389a0d..e7bd3e4aa97c 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -245,7 +245,30 @@ public final class HdmiControlManager {
}
/**
- * Gets an object that represents a HDMI-CEC logical device of type playback on the system.
+ * Gets an object that represents an HDMI-CEC logical device of a specified type.
+ *
+ * @param type CEC device type
+ * @return {@link HdmiClient} instance. {@code null} on failure.
+ * @see {@link HdmiCecDeviceInfo#DEVICE_PLAYBACK}
+ * @see {@link HdmiCecDeviceInfo#DEVICE_TV}
+ */
+ @Nullable
+ public HdmiClient getClient(int type) {
+ if (mService == null) {
+ return null;
+ }
+ switch (type) {
+ case HdmiCecDeviceInfo.DEVICE_TV:
+ return mHasTvDevice ? new HdmiTvClient(mService) : null;
+ case HdmiCecDeviceInfo.DEVICE_PLAYBACK:
+ return mHasPlaybackDevice ? new HdmiPlaybackClient(mService) : null;
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Gets an object that represents an HDMI-CEC logical device of type playback on the system.
*
* <p>Used to send HDMI control messages to other devices like TV or audio amplifier through
* HDMI bus. It is also possible to communicate with other logical devices hosted in the same
@@ -255,14 +278,11 @@ public final class HdmiControlManager {
*/
@Nullable
public HdmiPlaybackClient getPlaybackClient() {
- if (mService == null || !mHasPlaybackDevice) {
- return null;
- }
- return new HdmiPlaybackClient(mService);
+ return (HdmiPlaybackClient) getClient(HdmiCecDeviceInfo.DEVICE_PLAYBACK);
}
/**
- * Gets an object that represents a HDMI-CEC logical device of type TV on the system.
+ * Gets an object that represents an HDMI-CEC logical device of type TV on the system.
*
* <p>Used to send HDMI control messages to other devices and manage them through
* HDMI bus. It is also possible to communicate with other logical devices hosted in the same
@@ -272,10 +292,7 @@ public final class HdmiControlManager {
*/
@Nullable
public HdmiTvClient getTvClient() {
- if (mService == null || !mHasTvDevice) {
- return null;
- }
- return new HdmiTvClient(mService);
+ return (HdmiTvClient) getClient(HdmiCecDeviceInfo.DEVICE_TV);
}
/**
diff --git a/core/java/android/hardware/hdmi/HdmiTvClient.java b/core/java/android/hardware/hdmi/HdmiTvClient.java
index 2735108a3503..077a17edb59a 100644
--- a/core/java/android/hardware/hdmi/HdmiTvClient.java
+++ b/core/java/android/hardware/hdmi/HdmiTvClient.java
@@ -114,15 +114,14 @@ public final class HdmiTvClient extends HdmiClient {
/**
* Select a CEC logical device to be a new active source.
*
- * @param logicalAddress
- * @param callback
+ * @param logicalAddress logical address of the device to select
+ * @param callback callback to get the result with
+ * @throws {@link IllegalArgumentException} if the {@code callback} is null
*/
public void deviceSelect(int logicalAddress, @NonNull SelectCallback callback) {
if (callback == null) {
throw new IllegalArgumentException("callback must not be null.");
}
-
- // TODO: Replace SelectCallback with PartialResult.
try {
mService.deviceSelect(logicalAddress, getCallbackWrapper(callback));
} catch (RemoteException e) {
@@ -131,6 +130,24 @@ public final class HdmiTvClient extends HdmiClient {
}
/**
+ * Select a HDMI port to be a new route path.
+ *
+ * @param portId HDMI port to select
+ * @param callback callback to get the result with
+ * @throws {@link IllegalArgumentException} if the {@code callback} is null
+ */
+ public void portSelect(int portId, @NonNull SelectCallback callback) {
+ if (callback == null) {
+ throw new IllegalArgumentException("Callback must not be null");
+ }
+ try {
+ mService.portSelect(portId, getCallbackWrapper(callback));
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to select port: ", e);
+ }
+ }
+
+ /**
* Set system audio volume
*
* @param oldIndex current volume index
diff --git a/media/java/android/media/tv/ITvInputHardware.aidl b/media/java/android/media/tv/ITvInputHardware.aidl
index 3a041613ee1b..96223ba7bac1 100644
--- a/media/java/android/media/tv/ITvInputHardware.aidl
+++ b/media/java/android/media/tv/ITvInputHardware.aidl
@@ -35,9 +35,9 @@ interface ITvInputHardware {
boolean setSurface(in Surface surface, in TvStreamConfig config);
/**
- * Set volume for this stream via AudioGain. (TBD)
+ * Set volume for this stream via AudioGain.
*/
- void setVolume(float volume);
+ void setStreamVolume(float volume);
/**
* Dispatch key event to HDMI service. The events would be automatically converted to
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index 7b382c63f922..55f6ade51f40 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -153,7 +153,7 @@ abstract class HdmiCecLocalDevice {
/**
* Called once a logical address of the local device is allocated.
*/
- protected abstract void onAddressAllocated(int logicalAddress, boolean fromBootup);
+ protected abstract void onAddressAllocated(int logicalAddress, int reason);
/**
* Get the preferred logical address from system properties.
@@ -458,10 +458,10 @@ abstract class HdmiCecLocalDevice {
}
@ServiceThreadOnly
- final void handleAddressAllocated(int logicalAddress, boolean fromBootup) {
+ final void handleAddressAllocated(int logicalAddress, int reason) {
assertRunOnServiceThread();
mAddress = mPreferredAddress = logicalAddress;
- onAddressAllocated(logicalAddress, fromBootup);
+ onAddressAllocated(logicalAddress, reason);
setPreferredAddress(logicalAddress);
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index a66d78c0a3e8..b05f1afb619e 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -39,10 +39,15 @@ final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice {
@Override
@ServiceThreadOnly
- protected void onAddressAllocated(int logicalAddress, boolean fromBootup) {
+ protected void onAddressAllocated(int logicalAddress, int reason) {
assertRunOnServiceThread();
mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
mAddress, mService.getPhysicalAddress(), mDeviceType));
+ if (reason == HdmiControlService.INITIATED_BY_SCREEN_ON) {
+ oneTouchPlay(new IHdmiControlCallback.Stub() {
+ @Override public void onComplete(int result) throws RemoteException {}
+ });
+ }
}
@Override
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 8b2ed93fce90..8e64bfa9911d 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -120,13 +120,14 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
@Override
@ServiceThreadOnly
- protected void onAddressAllocated(int logicalAddress, boolean fromBootup) {
+ protected void onAddressAllocated(int logicalAddress, int reason) {
assertRunOnServiceThread();
mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
mAddress, mService.getPhysicalAddress(), mDeviceType));
mService.sendCecCommand(HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
mAddress, mService.getVendorId()));
- launchRoutingControl(fromBootup);
+ launchRoutingControl(reason != HdmiControlService.INITIATED_BY_ENABLE_CEC &&
+ reason != HdmiControlService.INITIATED_BY_BOOT_UP);
launchDeviceDiscovery();
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 435507de654c..e0673e971bb3 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -73,6 +73,12 @@ public final class HdmiControlService extends SystemService {
static final String PERMISSION = "android.permission.HDMI_CEC";
+ // The reason code to initiate intializeCec().
+ static final int INITIATED_BY_ENABLE_CEC = 0;
+ static final int INITIATED_BY_BOOT_UP = 1;
+ static final int INITIATED_BY_SCREEN_ON = 2;
+ static final int INITIATED_BY_WAKE_UP_MESSAGE = 3;
+
/**
* Interface to report send result.
*/
@@ -216,6 +222,9 @@ public final class HdmiControlService extends SystemService {
@ServiceThreadOnly
private boolean mStandbyMessageReceived = false;
+ @ServiceThreadOnly
+ private boolean mWakeUpMessageReceived = false;
+
public HdmiControlService(Context context) {
super(context);
mLocalDevices = HdmiUtils.asImmutableList(getContext().getResources().getIntArray(
@@ -237,7 +246,7 @@ public final class HdmiControlService extends SystemService {
// TODO: load value for mHdmiControlEnabled from preference.
if (mHdmiControlEnabled) {
- initializeCec(true);
+ initializeCec(INITIATED_BY_BOOT_UP);
}
} else {
Slog.i(TAG, "Device does not support HDMI-CEC.");
@@ -264,6 +273,11 @@ public final class HdmiControlService extends SystemService {
* Called when the initialization of local devices is complete.
*/
private void onInitializeCecComplete() {
+ if (mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON) {
+ mPowerStatus = HdmiControlManager.POWER_STATUS_ON;
+ }
+ mWakeUpMessageReceived = false;
+
if (isTvDevice()) {
mCecController.setOption(HdmiTvClient.OPTION_CEC_AUTO_WAKEUP,
tv().getAutoWakeup() ? HdmiTvClient.ENABLED : HdmiTvClient.DISABLED);
@@ -280,18 +294,18 @@ public final class HdmiControlService extends SystemService {
Global.putInt(cr, key, value ? Constants.TRUE : Constants.FALSE);
}
- private void initializeCec(boolean fromBootup) {
+ private void initializeCec(int initiatedBy) {
mCecController.setOption(HdmiTvClient.OPTION_CEC_SERVICE_CONTROL,
HdmiTvClient.ENABLED);
- initializeLocalDevices(mLocalDevices, fromBootup);
+ initializeLocalDevices(mLocalDevices, initiatedBy);
}
@ServiceThreadOnly
- private void initializeLocalDevices(final List<Integer> deviceTypes, final boolean fromBootup) {
+ private void initializeLocalDevices(final List<Integer> deviceTypes, final int initiatedBy) {
assertRunOnServiceThread();
// A container for [Logical Address, Local device info].
final SparseArray<HdmiCecLocalDevice> devices = new SparseArray<>();
- final SparseIntArray finished = new SparseIntArray();
+ final int[] finished = new int[1];
clearLocalDevices();
for (int type : deviceTypes) {
final HdmiCecLocalDevice localDevice = HdmiCecLocalDevice.create(this, type);
@@ -309,14 +323,11 @@ public final class HdmiControlService extends SystemService {
mCecController.addLogicalAddress(logicalAddress);
devices.append(logicalAddress, localDevice);
}
- finished.append(deviceType, logicalAddress);
// Address allocation completed for all devices. Notify each device.
- if (deviceTypes.size() == finished.size()) {
- if (mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON) {
- mPowerStatus = HdmiControlManager.POWER_STATUS_ON;
- }
- notifyAddressAllocated(devices, fromBootup);
+ if (deviceTypes.size() == ++finished[0]) {
+ onInitializeCecComplete();
+ notifyAddressAllocated(devices, initiatedBy);
}
}
});
@@ -324,15 +335,13 @@ public final class HdmiControlService extends SystemService {
}
@ServiceThreadOnly
- private void notifyAddressAllocated(SparseArray<HdmiCecLocalDevice> devices,
- boolean fromBootup) {
+ private void notifyAddressAllocated(SparseArray<HdmiCecLocalDevice> devices, int initiatedBy) {
assertRunOnServiceThread();
for (int i = 0; i < devices.size(); ++i) {
int address = devices.keyAt(i);
HdmiCecLocalDevice device = devices.valueAt(i);
- device.handleAddressAllocated(address, fromBootup);
+ device.handleAddressAllocated(address, initiatedBy);
}
- onInitializeCecComplete();
}
// Initialize HDMI port information. Combine the information from CEC and MHL HAL and
@@ -1412,6 +1421,7 @@ public final class HdmiControlService extends SystemService {
@ServiceThreadOnly
void wakeUp() {
assertRunOnServiceThread();
+ mWakeUpMessageReceived = true;
PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
pm.wakeUp(SystemClock.uptimeMillis());
// PowerManger will send the broadcast Intent.ACTION_SCREEN_ON and after this gets
@@ -1434,7 +1444,11 @@ public final class HdmiControlService extends SystemService {
mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
if (mCecController != null) {
if (mHdmiControlEnabled) {
- initializeCec(true);
+ int startReason = INITIATED_BY_SCREEN_ON;
+ if (mWakeUpMessageReceived) {
+ startReason = INITIATED_BY_WAKE_UP_MESSAGE;
+ }
+ initializeCec(startReason);
}
} else {
Slog.i(TAG, "Device does not support HDMI-CEC.");
@@ -1551,7 +1565,7 @@ public final class HdmiControlService extends SystemService {
}
if (enabled) {
- initializeCec(false);
+ initializeCec(INITIATED_BY_ENABLE_CEC);
} else {
disableDevices(new PendingActionClearedCallback() {
@Override
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index 4d375eed7086..ba79fed4bd21 100644
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -29,6 +29,8 @@ import android.hardware.hdmi.IHdmiHotplugEventListener;
import android.hardware.hdmi.IHdmiInputChangeListener;
import android.media.AudioDevicePort;
import android.media.AudioFormat;
+import android.media.AudioGain;
+import android.media.AudioGainConfig;
import android.media.AudioManager;
import android.media.AudioPatch;
import android.media.AudioPort;
@@ -55,6 +57,7 @@ import android.view.Surface;
import com.android.server.SystemService;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
@@ -559,6 +562,8 @@ class TvInputHardwareManager implements TvInputHal.Callback {
private final AudioDevicePort mAudioSource;
private AudioDevicePort mAudioSink;
private AudioPatch mAudioPatch = null;
+ private float mCommittedVolume = 0.0f;
+ private float mVolume = 0.0f;
private TvStreamConfig mActiveConfig = null;
@@ -663,9 +668,42 @@ class TvInputHardwareManager implements TvInputHal.Callback {
}
private void updateAudioPatchLocked() {
+ AudioGainConfig sourceGainConfig = null;
+ if (mAudioSource.gains().length > 0 && mVolume != mCommittedVolume) {
+ AudioGain sourceGain = null;
+ for (AudioGain gain : mAudioSource.gains()) {
+ if ((gain.mode() & AudioGain.MODE_JOINT) != 0) {
+ sourceGain = gain;
+ break;
+ }
+ }
+ // NOTE: we only change the source gain in MODE_JOINT here.
+ if (sourceGain != null) {
+ int steps = (sourceGain.maxValue() - sourceGain.minValue())
+ / sourceGain.stepValue();
+ int gainValue = sourceGain.minValue();
+ if (mVolume < 1.0f) {
+ gainValue += sourceGain.stepValue() * (int) (mVolume * steps + 0.5);
+ } else {
+ gainValue = sourceGain.maxValue();
+ }
+ int numChannels = 0;
+ for (int mask = sourceGain.channelMask(); mask > 0; mask >>= 1) {
+ numChannels += (mask & 1);
+ }
+ int[] gainValues = new int[numChannels];
+ Arrays.fill(gainValues, gainValue);
+ sourceGainConfig = sourceGain.buildConfig(AudioGain.MODE_JOINT,
+ sourceGain.channelMask(), gainValues, 0);
+ } else {
+ Slog.w(TAG, "No audio source gain with MODE_JOINT support exists.");
+ }
+ }
+
AudioPortConfig sourceConfig = mAudioSource.activeConfig();
AudioPortConfig sinkConfig = mAudioSink.activeConfig();
AudioPatch[] audioPatchArray = new AudioPatch[] { mAudioPatch };
+ boolean shouldRecreateAudioPatch = false;
if (sinkConfig == null
|| (mDesiredSamplingRate != 0
&& sinkConfig.samplingRate() != mDesiredSamplingRate)
@@ -675,26 +713,32 @@ class TvInputHardwareManager implements TvInputHal.Callback {
&& sinkConfig.format() != mDesiredFormat)) {
sinkConfig = mAudioSource.buildConfig(mDesiredSamplingRate, mDesiredChannelMask,
mDesiredFormat, null);
+ shouldRecreateAudioPatch = true;
}
- if (sourceConfig == null) {
+ if (sourceConfig == null || sourceGainConfig != null) {
sourceConfig = mAudioSource.buildConfig(sinkConfig.samplingRate(),
- sinkConfig.channelMask(), sinkConfig.format(), null);
+ sinkConfig.channelMask(), sinkConfig.format(), sourceGainConfig);
+ shouldRecreateAudioPatch = true;
+ }
+ if (shouldRecreateAudioPatch) {
+ mCommittedVolume = mVolume;
+ mAudioManager.createAudioPatch(
+ audioPatchArray,
+ new AudioPortConfig[] { sourceConfig },
+ new AudioPortConfig[] { sinkConfig });
+ mAudioPatch = audioPatchArray[0];
}
- mAudioManager.createAudioPatch(
- audioPatchArray,
- new AudioPortConfig[] { sourceConfig },
- new AudioPortConfig[] { sinkConfig });
- mAudioPatch = audioPatchArray[0];
}
@Override
- public void setVolume(float volume) throws RemoteException {
+ public void setStreamVolume(float volume) throws RemoteException {
synchronized (mImplLock) {
if (mReleased) {
throw new IllegalStateException("Device already released.");
}
+ mVolume = volume;
+ updateAudioPatchLocked();
}
- // TODO: Use AudioGain?
}
@Override