Add for System Audio Mode

Note that this is skeleton change and do not merge
till get full review from outside of CEC team.

This change introduce four apis for System Audio Mode

1. boolean canChangeSystemAudioMode()
  -  Whether to change system audio mode or not.

2. setSystemAudioMode(boolean enabled, IHdmiControlCallback callback);
  - Change system audio mode.

3. add/removeSystemAudioModeChangeLister.
  - Register/deregister listner for AudioModeChange.

4. getSystemAudioMode()
  - Whether to system audio is enabled or not.

Change-Id: I1e82365155a9f7f6c3ac5d9db4871cf6bad46865
This commit is contained in:
Jungshik Jang
2014-06-19 22:19:20 +09:00
parent 3722c32860
commit ea67c183fe
10 changed files with 239 additions and 21 deletions

View File

@@ -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 \

View File

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

View File

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

View File

@@ -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;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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