Merge "Add for System Audio Mode"

This commit is contained in:
Jungshik Jang
2014-06-23 06:44:41 +00:00
committed by Android (Google) Code Review
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) {