/* * 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.internal.util.IndentingPrintWriter; 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 void init() { super.init(); mIsActiveSource = false; } @Override @ServiceThreadOnly protected void onAddressAllocated(int logicalAddress, int reason) { assertRunOnServiceThread(); mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand( mAddress, mService.getPhysicalAddress(), mDeviceType)); startQueuedActions(); } @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(); // We'll not clear mIsActiveSource on the hotplug event to pass CETC 11.2.2-2 ~ 3. 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 and consecutively, // Then if there is no 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; } else { mIsActiveSource = false; } } 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(); } @Override protected void dump(final IndentingPrintWriter pw) { super.dump(pw); pw.println("mIsActiveSource: " + mIsActiveSource); } }