diff options
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 |