Home | History | Annotate | Download | only in hdmi
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package com.android.server.hdmi;
     17 
     18 import static android.hardware.hdmi.HdmiControlManager.POWER_STATUS_UNKNOWN;
     19 
     20 import android.hardware.hdmi.HdmiDeviceInfo;
     21 import android.util.SparseIntArray;
     22 
     23 import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
     24 
     25 import java.util.List;
     26 
     27 /**
     28  * Action that check each device's power status.
     29  */
     30 public class PowerStatusMonitorAction extends HdmiCecFeatureAction {
     31     private static final String TAG = "PowerStatusMonitorAction";
     32 
     33     // State that waits for <Report Power Status> once sending <Give Device Power Status>
     34     // to all external devices.
     35     private static final int STATE_WAIT_FOR_REPORT_POWER_STATUS = 1;
     36     // State that waits for next monitoring
     37     private static final int STATE_WAIT_FOR_NEXT_MONITORING = 2;
     38 
     39     private static final int INVALID_POWER_STATUS = POWER_STATUS_UNKNOWN - 1;
     40 
     41     // Monitoring interval (60s)
     42     private static final int MONITIROING_INTERNAL_MS = 60000;
     43 
     44     // Timeout once sending <Give Device Power Status>
     45     private static final int REPORT_POWER_STATUS_TIMEOUT_MS = 5000;
     46 
     47     // Container for current power status of all external devices.
     48     // The key is a logical address a device and the value is current power status of it
     49     // Whenever the action receives <Report Power Status> from a device,
     50     // it removes an entry of the given device.
     51     // If this is non-empty when timeout for STATE_WAIT_FOR_REPORT_POWER_STATUS happens,
     52     // updates power status of all remaining devices into POWER_STATUS_UNKNOWN.
     53     private final SparseIntArray mPowerStatus = new SparseIntArray();
     54 
     55     PowerStatusMonitorAction(HdmiCecLocalDevice source) {
     56         super(source);
     57     }
     58 
     59     @Override
     60     boolean start() {
     61         queryPowerStatus();
     62         return true;
     63     }
     64 
     65     @Override
     66     boolean processCommand(HdmiCecMessage cmd) {
     67         if (mState == STATE_WAIT_FOR_REPORT_POWER_STATUS
     68                 && cmd.getOpcode() == Constants.MESSAGE_REPORT_POWER_STATUS) {
     69             return handleReportPowerStatus(cmd);
     70         }
     71         return false;
     72     }
     73 
     74     private boolean handleReportPowerStatus(HdmiCecMessage cmd) {
     75         int sourceAddress = cmd.getSource();
     76         int oldStatus = mPowerStatus.get(sourceAddress, INVALID_POWER_STATUS);
     77         if (oldStatus == INVALID_POWER_STATUS) {
     78             // if no device exists for incoming message, hands it over to other actions.
     79             return false;
     80         }
     81         int newStatus = cmd.getParams()[0] & 0xFF;
     82         updatePowerStatus(sourceAddress, newStatus, true);
     83         return true;
     84     }
     85 
     86     @Override
     87     void handleTimerEvent(int state) {
     88         switch (mState) {
     89             case STATE_WAIT_FOR_NEXT_MONITORING:
     90                 queryPowerStatus();
     91                 break;
     92             case STATE_WAIT_FOR_REPORT_POWER_STATUS:
     93                 handleTimeout();
     94                 break;
     95         }
     96     }
     97 
     98     private void handleTimeout() {
     99         for (int i = 0; i < mPowerStatus.size(); ++i) {
    100             int logicalAddress = mPowerStatus.keyAt(i);
    101             updatePowerStatus(logicalAddress, POWER_STATUS_UNKNOWN, false);
    102         }
    103         mPowerStatus.clear();
    104         mState = STATE_WAIT_FOR_NEXT_MONITORING;
    105     }
    106 
    107     private void resetPowerStatus(List<HdmiDeviceInfo> deviceInfos) {
    108         mPowerStatus.clear();
    109         for (HdmiDeviceInfo info : deviceInfos) {
    110             mPowerStatus.append(info.getLogicalAddress(), info.getDevicePowerStatus());
    111         }
    112     }
    113 
    114     private void queryPowerStatus() {
    115         List<HdmiDeviceInfo> deviceInfos = tv().getDeviceInfoList(false);
    116         resetPowerStatus(deviceInfos);
    117         for (HdmiDeviceInfo info : deviceInfos) {
    118             final int logicalAddress = info.getLogicalAddress();
    119             sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(getSourceAddress(),
    120                     logicalAddress),
    121                     new SendMessageCallback() {
    122                         @Override
    123                         public void onSendCompleted(int error) {
    124                             // If fails to send <Give Device Power Status>,
    125                             // update power status into UNKNOWN.
    126                             if (error != Constants.SEND_RESULT_SUCCESS) {
    127                                updatePowerStatus(logicalAddress, POWER_STATUS_UNKNOWN, true);
    128                             }
    129                         }
    130                     });
    131         }
    132 
    133         mState = STATE_WAIT_FOR_REPORT_POWER_STATUS;
    134 
    135         // Add both timers, monitoring and timeout.
    136         addTimer(STATE_WAIT_FOR_NEXT_MONITORING, MONITIROING_INTERNAL_MS);
    137         addTimer(STATE_WAIT_FOR_REPORT_POWER_STATUS, REPORT_POWER_STATUS_TIMEOUT_MS);
    138     }
    139 
    140     private void updatePowerStatus(int logicalAddress, int newStatus, boolean remove) {
    141         tv().updateDevicePowerStatus(logicalAddress, newStatus);
    142 
    143         if (remove) {
    144             mPowerStatus.delete(logicalAddress);
    145         }
    146     }
    147 }
    148