From 410ca9c7a4a2d69af5c81e76320433bfda05cafe Mon Sep 17 00:00:00 2001 From: Jungshik Jang Date: Thu, 7 Aug 2014 18:04:14 +0900 Subject: [PATCH] Implement PowerStatusMonitorAction. After this change, need to add an api which reports power status change to other component like TIF. Bug: 16445063 Change-Id: I5a347057af6140ece82acb5d15e3a8b3779b18be --- .../server/hdmi/HdmiCecLocalDeviceTv.java | 20 +++ .../server/hdmi/HdmiControlService.java | 7 +- .../com/android/server/hdmi/HdmiUtils.java | 10 ++ .../server/hdmi/PowerStatusMonitorAction.java | 146 ++++++++++++++++++ 4 files changed, 181 insertions(+), 2 deletions(-) create mode 100644 services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java index fb4fa7f2a3a91..ad5b2ba8020eb 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java @@ -617,6 +617,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { } addAndStartAction(new HotplugDetectionAction(HdmiCecLocalDeviceTv.this)); + addAndStartAction(new PowerStatusMonitorAction(HdmiCecLocalDeviceTv.this)); // If there is AVR, initiate System Audio Auto initiation action, // which turns on and off system audio according to last system @@ -1300,6 +1301,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { // LocalDeviceTv.onAddressAllocated() -> launchDeviceDiscovery(). removeAction(DeviceDiscoveryAction.class); removeAction(HotplugDetectionAction.class); + removeAction(PowerStatusMonitorAction.class); // Remove recording actions. removeAction(OneTouchRecordAction.class); removeAction(TimerRecordingAction.class); @@ -1521,4 +1523,22 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { } }); } + + void updateDevicePowerStatus(int logicalAddress, int newPowerStatus) { + HdmiDeviceInfo info = getDeviceInfo(logicalAddress); + if (info == null) { + Slog.w(TAG, "Can not update power status of non-existing device:" + logicalAddress); + return; + } + + if (info.getDevicePowerStatus() == newPowerStatus) { + return; + } + + HdmiDeviceInfo newInfo = HdmiUtils.cloneHdmiDeviceInfo(info, newPowerStatus); + // addDeviceInfo replaces old device info with new one if exists. + addDeviceInfo(newInfo); + + // TODO: notify this update to others. + } } diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index cccc44c23e025..81b99f022bdaf 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -317,7 +317,10 @@ public final class HdmiControlService extends SystemService { if (logicalAddress == Constants.ADDR_UNREGISTERED) { Slog.e(TAG, "Failed to allocate address:[device_type:" + deviceType + "]"); } else { - HdmiDeviceInfo deviceInfo = createDeviceInfo(logicalAddress, deviceType); + // Set POWER_STATUS_ON to all local devices because they share lifetime + // with system. + HdmiDeviceInfo deviceInfo = createDeviceInfo(logicalAddress, deviceType, + HdmiControlManager.POWER_STATUS_ON); localDevice.setDeviceInfo(deviceInfo); mCecController.addLocalDevice(deviceType, localDevice); mCecController.addLogicalAddress(logicalAddress); @@ -653,7 +656,7 @@ public final class HdmiControlService extends SystemService { } } - private HdmiDeviceInfo createDeviceInfo(int logicalAddress, int deviceType) { + private HdmiDeviceInfo createDeviceInfo(int logicalAddress, int deviceType, int powerStatus) { // TODO: find better name instead of model name. String displayName = Build.MODEL; return new HdmiDeviceInfo(logicalAddress, diff --git a/services/core/java/com/android/server/hdmi/HdmiUtils.java b/services/core/java/com/android/server/hdmi/HdmiUtils.java index a52e0d2aac200..23f19ff5944b1 100644 --- a/services/core/java/com/android/server/hdmi/HdmiUtils.java +++ b/services/core/java/com/android/server/hdmi/HdmiUtils.java @@ -266,4 +266,14 @@ final class HdmiUtils { } return true; } + + /** + * Clone {@link HdmiDeviceInfo} with new power status. + */ + static HdmiDeviceInfo cloneHdmiDeviceInfo(HdmiDeviceInfo info, int newPowerStatus) { + return new HdmiDeviceInfo(info.getLogicalAddress(), + info.getPhysicalAddress(), info.getPortId(), info.getDeviceType(), + info.getVendorId(), info.getDisplayName(), newPowerStatus); + } + } diff --git a/services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java b/services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java new file mode 100644 index 0000000000000..03fbb95624208 --- /dev/null +++ b/services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java @@ -0,0 +1,146 @@ +/* + * 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 static android.hardware.hdmi.HdmiControlManager.POWER_STATUS_UNKNOWN; + +import android.hardware.hdmi.HdmiDeviceInfo; +import android.util.SparseIntArray; + +import com.android.server.hdmi.HdmiControlService.SendMessageCallback; + +import java.util.List; + +/** + * Action that check each device's power status. + */ +public class PowerStatusMonitorAction extends HdmiCecFeatureAction { + private static final String TAG = "PowerStatusMonitorAction"; + + // State that waits for once sending + // to all external devices. + private static final int STATE_WAIT_FOR_REPORT_POWER_STATUS = 1; + // State that waits for next monitoring + private static final int STATE_WAIT_FOR_NEXT_MONITORING = 2; + + private static final int INVALID_POWER_STATUS = POWER_STATUS_UNKNOWN - 1; + + // Monitoring interval (60s) + private static final int MONITIROING_INTERNAL_MS = 60000; + + // Timeout once sending + private static final int REPORT_POWER_STATUS_TIMEOUT_MS = 5000; + + // Container for current power status of all external devices. + // The key is a logical address a device and the value is current power status of it + // Whenever the action receives from a device, + // it removes an entry of the given device. + // If this is non-empty when timeout for STATE_WAIT_FOR_REPORT_POWER_STATUS happens, + // updates power status of all remaining devices into POWER_STATUS_UNKNOWN. + private final SparseIntArray mPowerStatus = new SparseIntArray(); + + PowerStatusMonitorAction(HdmiCecLocalDevice source) { + super(source); + } + + @Override + boolean start() { + queryPowerStatus(); + return true; + } + + @Override + boolean processCommand(HdmiCecMessage cmd) { + if (mState != STATE_WAIT_FOR_REPORT_POWER_STATUS) { + return false; + } + return handleReportPowerStatus(cmd); + } + + private boolean handleReportPowerStatus(HdmiCecMessage cmd) { + int sourceAddress = cmd.getSource(); + int oldStatus = mPowerStatus.get(sourceAddress, INVALID_POWER_STATUS); + if (oldStatus == INVALID_POWER_STATUS) { + // if no device exists for incoming message, hands it over to other actions. + return false; + } + int newStatus = cmd.getParams()[0]; + updatePowerStatus(sourceAddress, newStatus, true); + return true; + } + + @Override + void handleTimerEvent(int state) { + switch (mState) { + case STATE_WAIT_FOR_NEXT_MONITORING: + queryPowerStatus(); + break; + case STATE_WAIT_FOR_REPORT_POWER_STATUS: + handleTimeout(); + break; + } + } + + private void handleTimeout() { + for (int i = 0; i < mPowerStatus.size(); ++i) { + int logicalAddress = mPowerStatus.keyAt(i); + updatePowerStatus(logicalAddress, POWER_STATUS_UNKNOWN, false); + } + mPowerStatus.clear(); + mState = STATE_WAIT_FOR_NEXT_MONITORING; + } + + private void resetPowerStatus(List deviceInfos) { + mPowerStatus.clear(); + for (HdmiDeviceInfo info : deviceInfos) { + mPowerStatus.append(info.getLogicalAddress(), info.getDevicePowerStatus()); + } + } + + private void queryPowerStatus() { + List deviceInfos = tv().getDeviceInfoList(false); + resetPowerStatus(deviceInfos); + for (HdmiDeviceInfo info : deviceInfos) { + final int logicalAddress = info.getLogicalAddress(); + sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(getSourceAddress(), + logicalAddress), + new SendMessageCallback() { + @Override + public void onSendCompleted(int error) { + // If fails to send , + // update power status into UNKNOWN. + if (error != Constants.SEND_RESULT_SUCCESS) { + updatePowerStatus(logicalAddress, POWER_STATUS_UNKNOWN, true); + } + } + }); + } + + mState = STATE_WAIT_FOR_REPORT_POWER_STATUS; + + // Add both timers, monitoring and timeout. + addTimer(STATE_WAIT_FOR_NEXT_MONITORING, MONITIROING_INTERNAL_MS); + addTimer(STATE_WAIT_FOR_REPORT_POWER_STATUS, REPORT_POWER_STATUS_TIMEOUT_MS); + } + + private void updatePowerStatus(int logicalAddress, int newStatus, boolean remove) { + tv().updateDevicePowerStatus(logicalAddress, newStatus); + + if (remove) { + mPowerStatus.delete(logicalAddress); + } + } +}