blob: 5a2fa9c7882b6a449b4fa75419f8e23aea94e9d4 [file] [log] [blame]
/*
* 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 com.android.server.hdmi;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.IHdmiControlCallback;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.util.Slog;
import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
/**
* Represent a logical device of type Playback residing in Android system.
*/
final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice {
private static final String TAG = "HdmiCecLocalDevicePlayback";
private boolean mIsActiveSource = false;
HdmiCecLocalDevicePlayback(HdmiControlService service) {
super(service, HdmiDeviceInfo.DEVICE_PLAYBACK);
}
@Override
@ServiceThreadOnly
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
@ServiceThreadOnly
protected int getPreferredAddress() {
assertRunOnServiceThread();
return SystemProperties.getInt(Constants.PROPERTY_PREFERRED_ADDRESS_PLAYBACK,
Constants.ADDR_UNREGISTERED);
}
@Override
@ServiceThreadOnly
protected void setPreferredAddress(int addr) {
assertRunOnServiceThread();
SystemProperties.set(Constants.PROPERTY_PREFERRED_ADDRESS_PLAYBACK,
String.valueOf(addr));
}
@ServiceThreadOnly
void oneTouchPlay(IHdmiControlCallback callback) {
assertRunOnServiceThread();
if (hasAction(OneTouchPlayAction.class)) {
Slog.w(TAG, "oneTouchPlay already in progress");
invokeCallback(callback, HdmiControlManager.RESULT_ALREADY_IN_PROGRESS);
return;
}
// TODO: Consider the case of multiple TV sets. For now we always direct the command
// to the primary one.
OneTouchPlayAction action = OneTouchPlayAction.create(this, Constants.ADDR_TV,
callback);
if (action == null) {
Slog.w(TAG, "Cannot initiate oneTouchPlay");
invokeCallback(callback, HdmiControlManager.RESULT_EXCEPTION);
return;
}
addAndStartAction(action);
}
@ServiceThreadOnly
void queryDisplayStatus(IHdmiControlCallback callback) {
assertRunOnServiceThread();
if (hasAction(DevicePowerStatusAction.class)) {
Slog.w(TAG, "queryDisplayStatus already in progress");
invokeCallback(callback, HdmiControlManager.RESULT_ALREADY_IN_PROGRESS);
return;
}
DevicePowerStatusAction action = DevicePowerStatusAction.create(this,
Constants.ADDR_TV, callback);
if (action == null) {
Slog.w(TAG, "Cannot initiate queryDisplayStatus");
invokeCallback(callback, HdmiControlManager.RESULT_EXCEPTION);
return;
}
addAndStartAction(action);
}
@ServiceThreadOnly
private void invokeCallback(IHdmiControlCallback callback, int result) {
assertRunOnServiceThread();
try {
callback.onComplete(result);
} catch (RemoteException e) {
Slog.e(TAG, "Invoking callback failed:" + e);
}
}
@Override
@ServiceThreadOnly
void onHotplug(int portId, boolean connected) {
assertRunOnServiceThread();
mCecMessageCache.flushAll();
mIsActiveSource = false;
if (connected && mService.isPowerStandbyOrTransient()) {
mService.wakeUp();
}
}
@ServiceThreadOnly
void markActiveSource() {
assertRunOnServiceThread();
mIsActiveSource = true;
}
@Override
@ServiceThreadOnly
protected boolean handleActiveSource(HdmiCecMessage message) {
assertRunOnServiceThread();
int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
mayResetActiveSource(physicalAddress);
return true; // Broadcast message.
}
private void mayResetActiveSource(int physicalAddress) {
if (physicalAddress != mService.getPhysicalAddress()) {
mIsActiveSource = false;
}
}
@Override
@ServiceThreadOnly
protected boolean handleSetStreamPath(HdmiCecMessage message) {
assertRunOnServiceThread();
int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
maySetActiveSource(physicalAddress);
maySendActiveSource();
wakeUpIfActiveSource();
return true; // Broadcast message.
}
// Samsung model, we tested, sends <RoutingChange> and <RequestActiveSource> consecutively,
// Then if there is no <ActiveSource> response, it will change the input to
// the internal source. To handle this, we'll set ActiveSource aggressively.
@Override
@ServiceThreadOnly
protected boolean handleRoutingChange(HdmiCecMessage message) {
assertRunOnServiceThread();
int newPath = HdmiUtils.twoBytesToInt(message.getParams(), 2);
maySetActiveSource(newPath);
return true; // Broadcast message.
}
@Override
@ServiceThreadOnly
protected boolean handleRoutingInformation(HdmiCecMessage message) {
assertRunOnServiceThread();
int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
maySetActiveSource(physicalAddress);
return true; // Broadcast message.
}
private void maySetActiveSource(int physicalAddress) {
if (physicalAddress == mService.getPhysicalAddress()) {
mIsActiveSource = true;
}
}
private void wakeUpIfActiveSource() {
if (mIsActiveSource && mService.isPowerStandbyOrTransient()) {
mService.wakeUp();
}
}
private void maySendActiveSource() {
if (mIsActiveSource) {
mService.sendCecCommand(HdmiCecMessageBuilder.buildActiveSource(
mAddress, mService.getPhysicalAddress()));
}
}
@Override
@ServiceThreadOnly
protected boolean handleRequestActiveSource(HdmiCecMessage message) {
assertRunOnServiceThread();
maySendActiveSource();
return true; // Broadcast message.
}
@Override
@ServiceThreadOnly
protected void disableDevice(boolean initiatedByCec, PendingActionClearedCallback callback) {
super.disableDevice(initiatedByCec, callback);
assertRunOnServiceThread();
if (!initiatedByCec && mIsActiveSource) {
mService.sendCecCommand(HdmiCecMessageBuilder.buildInactiveSource(
mAddress, mService.getPhysicalAddress()));
}
mIsActiveSource = false;
checkIfPendingActionsCleared();
}
}