diff options
10 files changed, 239 insertions, 21 deletions
diff --git a/Android.mk b/Android.mk index 8f7779ea2c32..936b41a38c86 100644 --- a/Android.mk +++ b/Android.mk @@ -152,6 +152,7 @@ LOCAL_SRC_FILES += \ core/java/android/hardware/hdmi/IHdmiControlService.aidl \ core/java/android/hardware/hdmi/IHdmiDeviceEventListener.aidl \ core/java/android/hardware/hdmi/IHdmiHotplugEventListener.aidl \ + core/java/android/hardware/hdmi/IHdmiSystemAudioModeChangeListener.aidl \ core/java/android/hardware/input/IInputManager.aidl \ core/java/android/hardware/input/IInputDevicesChangedListener.aidl \ core/java/android/hardware/location/IFusedLocationHardware.aidl \ diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl index 559a469e5269..73726fa49843 100644 --- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl +++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl @@ -21,6 +21,7 @@ import android.hardware.hdmi.HdmiPortInfo; import android.hardware.hdmi.IHdmiControlCallback; import android.hardware.hdmi.IHdmiDeviceEventListener; import android.hardware.hdmi.IHdmiHotplugEventListener; +import android.hardware.hdmi.IHdmiSystemAudioModeChangeListener; import java.util.List; @@ -42,4 +43,9 @@ interface IHdmiControlService { void portSelect(int portId, IHdmiControlCallback callback); void sendKeyEvent(int keyCode, boolean isPressed); List<HdmiPortInfo> getPortInfo(); + boolean canChangeSystemAudioMode(); + boolean getSystemAudioMode(); + void setSystemAudioMode(boolean enabled, IHdmiControlCallback callback); + void addSystemAudioModeChangeListener(IHdmiSystemAudioModeChangeListener listener); + void removeSystemAudioModeChangeListener(IHdmiSystemAudioModeChangeListener listener); } diff --git a/core/java/android/hardware/hdmi/IHdmiSystemAudioModeChangeListener.aidl b/core/java/android/hardware/hdmi/IHdmiSystemAudioModeChangeListener.aidl new file mode 100644 index 000000000000..714bbe720ae7 --- /dev/null +++ b/core/java/android/hardware/hdmi/IHdmiSystemAudioModeChangeListener.aidl @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2014 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 android.hardware.hdmi; + +/** + * Callback interface definition for HDMI client to get informed of + * "System Audio" mode change. + * + * @hide + */ +oneway interface IHdmiSystemAudioModeChangeListener { + + /** + * @param enabled true if the device gets activated + */ + void onStatusChanged(in boolean enabled); +} diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java index 52c092cd7e05..1210e1029410 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java @@ -253,20 +253,35 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { mDeviceInfos.clear(); } + void changeSystemAudioMode(boolean enabled, IHdmiControlCallback callback) { + assertRunOnServiceThread(); + HdmiCecDeviceInfo avr = getAvrDeviceInfo(); + if (avr == null) { + invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE); + return; + } + + addAndStartAction( + new SystemAudioActionFromTv(this, avr.getLogicalAddress(), enabled, callback)); + } + + boolean canChangeSystemAudioMode() { + // TODO: once have immutable device info, test whether avr info exists or not. + return false; + } + void setSystemAudioMode(boolean on) { synchronized (mLock) { if (on != mSystemAudioMode) { mSystemAudioMode = on; // TODO: Need to set the preference for SystemAudioMode. - // TODO: Need to handle the notification of changing the mode and - // to identify the notification should be handled in the service or TvSettings. + mService.announceSystemAudioModeChange(on); } } } boolean getSystemAudioMode() { synchronized (mLock) { - assertRunOnServiceThread(); return mSystemAudioMode; } } diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index d323f34ed101..77748786f483 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -27,6 +27,7 @@ import android.hardware.hdmi.IHdmiControlCallback; import android.hardware.hdmi.IHdmiControlService; import android.hardware.hdmi.IHdmiDeviceEventListener; import android.hardware.hdmi.IHdmiHotplugEventListener; +import android.hardware.hdmi.IHdmiSystemAudioModeChangeListener; import android.os.Build; import android.os.Handler; import android.os.HandlerThread; @@ -113,6 +114,14 @@ public final class HdmiControlService extends SystemService { private final ArrayList<DeviceEventListenerRecord> mDeviceEventListenerRecords = new ArrayList<>(); + // List of listeners registered by callers that want to get notified of + // system audio mode changes. + private final ArrayList<IHdmiSystemAudioModeChangeListener> + mSystemAudioModeChangeListeners = new ArrayList<>(); + // List of records for system audio mode change to handle the the caller killed in action. + private final ArrayList<SystemAudioModeChangeListenerRecord> + mSystemAudioModeChangeListenerRecords = new ArrayList<>(); + // Handler used to run a task in service thread. private final Handler mHandler = new Handler(); @@ -447,6 +456,12 @@ public final class HdmiControlService extends SystemService { // TODO: Hook up with AudioManager. } + void announceSystemAudioModeChange(boolean enabled) { + for (IHdmiSystemAudioModeChangeListener listener : mSystemAudioModeChangeListeners) { + invokeSystemAudioModeChange(listener, enabled); + } + } + private HdmiCecDeviceInfo createDeviceInfo(int logicalAddress, int deviceType) { // TODO: find better name instead of model name. String displayName = Build.MODEL; @@ -480,7 +495,7 @@ public final class HdmiControlService extends SystemService { } @Override - public void binderDied() { + public void binderDied() { synchronized (mLock) { mDeviceEventListenerRecords.remove(this); mDeviceEventListeners.remove(mListener); @@ -488,6 +503,22 @@ public final class HdmiControlService extends SystemService { } } + private final class SystemAudioModeChangeListenerRecord implements IBinder.DeathRecipient { + private IHdmiSystemAudioModeChangeListener mListener; + + public SystemAudioModeChangeListenerRecord(IHdmiSystemAudioModeChangeListener listener) { + mListener = listener; + } + + @Override + public void binderDied() { + synchronized (mLock) { + mSystemAudioModeChangeListenerRecords.remove(this); + mSystemAudioModeChangeListeners.remove(mListener); + } + } + } + private void enforceAccessPermission() { getContext().enforceCallingOrSelfPermission(PERMISSION, TAG); } @@ -605,7 +636,7 @@ public final class HdmiControlService extends SystemService { enforceAccessPermission(); runOnServiceThread(new Runnable() { @Override - public void run() { + public void run() { HdmiControlService.this.addDeviceEventListener(listener); } }); @@ -616,6 +647,57 @@ public final class HdmiControlService extends SystemService { enforceAccessPermission(); return mPortInfo; } + + @Override + public boolean canChangeSystemAudioMode() { + enforceAccessPermission(); + HdmiCecLocalDeviceTv tv = tv(); + if (tv == null) { + return false; + } + return tv.canChangeSystemAudioMode(); + } + + @Override + public boolean getSystemAudioMode() { + enforceAccessPermission(); + HdmiCecLocalDeviceTv tv = tv(); + if (tv == null) { + return false; + } + return tv.getSystemAudioMode(); + } + + @Override + public void setSystemAudioMode(final boolean enabled, final IHdmiControlCallback callback) { + enforceAccessPermission(); + runOnServiceThread(new Runnable() { + @Override + public void run() { + HdmiCecLocalDeviceTv tv = tv(); + if (tv == null) { + Slog.w(TAG, "Local tv device not available"); + invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE); + return; + } + tv.changeSystemAudioMode(enabled, callback); + } + }); + } + + @Override + public void addSystemAudioModeChangeListener( + final IHdmiSystemAudioModeChangeListener listener) { + enforceAccessPermission(); + HdmiControlService.this.addSystemAudioModeChangeListner(listener); + } + + @Override + public void removeSystemAudioModeChangeListener( + final IHdmiSystemAudioModeChangeListener listener) { + enforceAccessPermission(); + HdmiControlService.this.removeSystemAudioModeChangeListener(listener); + } } private void oneTouchPlay(final IHdmiControlCallback callback) { @@ -693,6 +775,35 @@ public final class HdmiControlService extends SystemService { } } + private void addSystemAudioModeChangeListner(IHdmiSystemAudioModeChangeListener listener) { + SystemAudioModeChangeListenerRecord record = new SystemAudioModeChangeListenerRecord( + listener); + try { + listener.asBinder().linkToDeath(record, 0); + } catch (RemoteException e) { + Slog.w(TAG, "Listener already died"); + return; + } + synchronized (mLock) { + mSystemAudioModeChangeListeners.add(listener); + mSystemAudioModeChangeListenerRecords.add(record); + } + } + + private void removeSystemAudioModeChangeListener(IHdmiSystemAudioModeChangeListener listener) { + synchronized (mLock) { + for (SystemAudioModeChangeListenerRecord record : + mSystemAudioModeChangeListenerRecords) { + if (record.mListener.asBinder() == listener) { + listener.asBinder().unlinkToDeath(record, 0); + mSystemAudioModeChangeListenerRecords.remove(record); + break; + } + } + mSystemAudioModeChangeListeners.remove(listener); + } + } + private void invokeCallback(IHdmiControlCallback callback, int result) { try { callback.onComplete(result); @@ -701,6 +812,15 @@ public final class HdmiControlService extends SystemService { } } + private void invokeSystemAudioModeChange(IHdmiSystemAudioModeChangeListener listener, + boolean enabled) { + try { + listener.onStatusChanged(enabled); + } catch (RemoteException e) { + Slog.e(TAG, "Invoking callback failed:" + e); + } + } + private void announceHotplugEvent(int portId, boolean connected) { HdmiHotplugEvent event = new HdmiHotplugEvent(portId, connected); synchronized (mLock) { diff --git a/services/core/java/com/android/server/hdmi/SystemAudioAction.java b/services/core/java/com/android/server/hdmi/SystemAudioAction.java index 92418abf6d5f..959a38e83ee9 100644 --- a/services/core/java/com/android/server/hdmi/SystemAudioAction.java +++ b/services/core/java/com/android/server/hdmi/SystemAudioAction.java @@ -16,8 +16,12 @@ package com.android.server.hdmi; +import android.annotation.Nullable; import android.hardware.hdmi.HdmiCec; import android.hardware.hdmi.HdmiCecMessage; +import android.hardware.hdmi.IHdmiControlCallback; +import android.os.RemoteException; +import android.util.Slog; /** * Base feature action class for SystemAudioActionFromTv and SystemAudioActionFromAvr. @@ -39,6 +43,8 @@ abstract class SystemAudioAction extends FeatureAction { // The target audio status of the action, whether to enable the system audio mode or not. protected boolean mTargetAudioStatus; + @Nullable private final IHdmiControlCallback mCallback; + private int mSendRetryCount = 0; /** @@ -47,13 +53,16 @@ abstract class SystemAudioAction extends FeatureAction { * @param source {@link HdmiCecLocalDevice} instance * @param avrAddress logical address of AVR device * @param targetStatus Whether to enable the system audio mode or not + * @param callback callback interface to be notified when it's done * @throw IllegalArugmentException if device type of sourceAddress and avrAddress is invalid */ - SystemAudioAction(HdmiCecLocalDevice source, int avrAddress, boolean targetStatus) { + SystemAudioAction(HdmiCecLocalDevice source, int avrAddress, boolean targetStatus, + IHdmiControlCallback callback) { super(source); HdmiUtils.verifyAddressType(avrAddress, HdmiCec.DEVICE_AUDIO_SYSTEM); mAvrLogicalAddress = avrAddress; mTargetAudioStatus = targetStatus; + mCallback = callback; } protected void sendSystemAudioModeRequest() { @@ -69,7 +78,7 @@ abstract class SystemAudioAction extends FeatureAction { addTimer(mState, mTargetAudioStatus ? ON_TIMEOUT_MS : OFF_TIMEOUT_MS); } else { setSystemAudioMode(false); - finish(); + finishWithCallback(HdmiCec.RESULT_EXCEPTION); } } }); @@ -79,7 +88,7 @@ abstract class SystemAudioAction extends FeatureAction { if (!mTargetAudioStatus // Don't retry for Off case. || mSendRetryCount++ >= MAX_SEND_RETRY_COUNT) { setSystemAudioMode(false); - finish(); + finishWithCallback(HdmiCec.RESULT_TIMEOUT); return; } sendSystemAudioModeRequest(); @@ -107,7 +116,7 @@ abstract class SystemAudioAction extends FeatureAction { // Unexpected response, consider the request is newly initiated by AVR. // To return 'false' will initiate new SystemAudioActionFromAvr by the control // service. - finish(); + finishWithCallback(HdmiCec.RESULT_EXCEPTION); return false; } default: @@ -116,7 +125,7 @@ abstract class SystemAudioAction extends FeatureAction { } protected void startAudioStatusAction() { - addAndStartAction(new SystemAudioStatusAction(tv(), mAvrLogicalAddress)); + addAndStartAction(new SystemAudioStatusAction(tv(), mAvrLogicalAddress, mCallback)); finish(); } @@ -136,4 +145,17 @@ abstract class SystemAudioAction extends FeatureAction { return; } } + + // TODO: if IHdmiControlCallback is general to other FeatureAction, + // move it into FeatureAction. + protected void finishWithCallback(int returnCode) { + if (mCallback != null) { + try { + mCallback.onComplete(returnCode); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to invoke callback.", e); + } + } + finish(); + } } diff --git a/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java b/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java index b743c64571b9..9d345897bf7b 100644 --- a/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java +++ b/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java @@ -17,6 +17,7 @@ package com.android.server.hdmi; import android.hardware.hdmi.HdmiCec; +import android.hardware.hdmi.IHdmiControlCallback; /** * Feature action that handles System Audio initiated by AVR devices. @@ -28,11 +29,12 @@ final class SystemAudioActionFromAvr extends SystemAudioAction { * @param source {@link HdmiCecLocalDevice} instance * @param avrAddress logical address of AVR device * @param targetStatus Whether to enable the system audio mode or not + * @param callback callback interface to be notified when it's done * @throw IllegalArugmentException if device type of tvAddress and avrAddress is invalid */ SystemAudioActionFromAvr(HdmiCecLocalDevice source, int avrAddress, - boolean targetStatus) { - super(source, avrAddress, targetStatus); + boolean targetStatus, IHdmiControlCallback callback) { + super(source, avrAddress, targetStatus, callback); HdmiUtils.verifyAddressType(getSourceAddress(), HdmiCec.DEVICE_TV); } @@ -45,7 +47,7 @@ final class SystemAudioActionFromAvr extends SystemAudioAction { private void handleSystemAudioActionFromAvr() { if (mTargetAudioStatus == tv().getSystemAudioMode()) { - finish(); + finishWithCallback(HdmiCec.RESULT_SUCCESS); return; } if (tv().isInPresetInstallationMode()) { @@ -62,7 +64,7 @@ final class SystemAudioActionFromAvr extends SystemAudioAction { startAudioStatusAction(); } else { setSystemAudioMode(false); - finish(); + finishWithCallback(HdmiCec.RESULT_SUCCESS); } } } diff --git a/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java b/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java index e0c4ff47a2f7..2d8f3fcc832d 100644 --- a/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java +++ b/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java @@ -17,6 +17,7 @@ package com.android.server.hdmi; import android.hardware.hdmi.HdmiCec; +import android.hardware.hdmi.IHdmiControlCallback; /** @@ -29,11 +30,12 @@ final class SystemAudioActionFromTv extends SystemAudioAction { * @param sourceAddress {@link HdmiCecLocalDevice} instance * @param avrAddress logical address of AVR device * @param targetStatus Whether to enable the system audio mode or not + * @param callback callback interface to be notified when it's done * @throw IllegalArugmentException if device type of tvAddress is invalid */ SystemAudioActionFromTv(HdmiCecLocalDevice sourceAddress, int avrAddress, - boolean targetStatus) { - super(sourceAddress, avrAddress, targetStatus); + boolean targetStatus, IHdmiControlCallback callback) { + super(sourceAddress, avrAddress, targetStatus, callback); HdmiUtils.verifyAddressType(getSourceAddress(), HdmiCec.DEVICE_TV); } diff --git a/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java b/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java index e4d82ef88eaa..c7ab406f7207 100644 --- a/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java +++ b/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java @@ -77,14 +77,14 @@ final class SystemAudioAutoInitiationAction extends FeatureAction { // If the last setting is system audio, turn on system audio whatever AVR status is. if (tv().getSystemAudioMode()) { if (canChangeSystemAudio()) { - addAndStartAction(new SystemAudioActionFromTv(tv(), mAvrAddress, true)); + addAndStartAction(new SystemAudioActionFromTv(tv(), mAvrAddress, true, null)); } } else { // If the last setting is non-system audio, turn off system audio mode // and update system audio status (volume or mute). tv().setSystemAudioMode(false); if (canChangeSystemAudio()) { - addAndStartAction(new SystemAudioStatusAction(tv(), mAvrAddress)); + addAndStartAction(new SystemAudioStatusAction(tv(), mAvrAddress, null)); } } finish(); diff --git a/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java b/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java index 5f4fc23dfe03..89206a71abb0 100644 --- a/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java +++ b/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java @@ -16,8 +16,11 @@ package com.android.server.hdmi; +import android.annotation.Nullable; import android.hardware.hdmi.HdmiCec; import android.hardware.hdmi.HdmiCecMessage; +import android.hardware.hdmi.IHdmiControlCallback; +import android.os.RemoteException; import android.util.Slog; import com.android.server.hdmi.HdmiControlService.SendMessageCallback; @@ -32,10 +35,13 @@ final class SystemAudioStatusAction extends FeatureAction { private static final int STATE_WAIT_FOR_REPORT_AUDIO_STATUS = 1; private final int mAvrAddress; + @Nullable private final IHdmiControlCallback mCallback; - SystemAudioStatusAction(HdmiCecLocalDevice source, int avrAddress) { + SystemAudioStatusAction(HdmiCecLocalDevice source, int avrAddress, + IHdmiControlCallback callback) { super(source); mAvrAddress = avrAddress; + mCallback = callback; } @Override @@ -67,7 +73,9 @@ final class SystemAudioStatusAction extends FeatureAction { ? HdmiConstants.UI_COMMAND_RESTORE_VOLUME_FUNCTION // SystemAudioMode: ON : HdmiConstants.UI_COMMAND_MUTE_FUNCTION; // SystemAudioMode: OFF sendUserControlPressedAndReleased(uiCommand); - finish(); + + // Still return SUCCESS to callback. + finishWithCallback(HdmiCec.RESULT_SUCCESS); } private void sendUserControlPressedAndReleased(int uiCommand) { @@ -103,7 +111,7 @@ final class SystemAudioStatusAction extends FeatureAction { // Toggle AVR's mute status to match with the system audio status. sendUserControlPressedAndReleased(HdmiConstants.UI_COMMAND_MUTE); } - finish(); + finishWithCallback(HdmiCec.RESULT_SUCCESS); } else { Slog.e(TAG, "Invalid <Report Audio Status> message:" + cmd); handleSendGiveAudioStatusFailure(); @@ -111,6 +119,17 @@ final class SystemAudioStatusAction extends FeatureAction { } } + private void finishWithCallback(int returnCode) { + if (mCallback != null) { + try { + mCallback.onComplete(returnCode); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to invoke callback.", e); + } + } + finish(); + } + @Override void handleTimerEvent(int state) { if (mState != state) { |