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.hardware.tv.cec.V1_0.SendMessageResult;
     22 import android.util.SparseIntArray;
     23 import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
     24 import java.util.List;
     25 
     26 /**
     27  * Action that check each device's power status.
     28  */
     29 public class PowerStatusMonitorAction extends HdmiCecFeatureAction {
     30     private static final String TAG = "PowerStatusMonitorAction";
     31 
     32     // State that waits for <Report Power Status> once sending <Give Device Power Status>
     33     // to all external devices.
     34     private static final int STATE_WAIT_FOR_REPORT_POWER_STATUS = 1;
     35     // State that waits for next monitoring
     36     private static final int STATE_WAIT_FOR_NEXT_MONITORING = 2;
     37 
     38     private static final int INVALID_POWER_STATUS = POWER_STATUS_UNKNOWN - 1;
     39 
     40     // Monitoring interval (60s)
     41     private static final int MONITIROING_INTERNAL_MS = 60000;
     42 
     43     // Timeout once sending <Give Device Power Status>
     44     private static final int REPORT_POWER_STATUS_TIMEOUT_MS = 5000;
     45 
     46     // Container for current power status of all external devices.
     47     // The key is a logical address a device and the value is current power status of it
     48     // Whenever the action receives <Report Power Status> from a device,
     49     // it removes an entry of the given device.
     50     // If this is non-empty when timeout for STATE_WAIT_FOR_REPORT_POWER_STATUS happens,
     51     // updates power status of all remaining devices into POWER_STATUS_UNKNOWN.
     52     private final SparseIntArray mPowerStatus = new SparseIntArray();
     53 
     54     PowerStatusMonitorAction(HdmiCecLocalDevice source) {
     55         super(source);
     56     }
     57 
     58     @Override
     59     boolean start() {
     60         queryPowerStatus();
     61         return true;
     62     }
     63 
     64     @Override
     65     boolean processCommand(HdmiCecMessage cmd) {
     66         if (mState == STATE_WAIT_FOR_REPORT_POWER_STATUS
     67                 && cmd.getOpcode() == Constants.MESSAGE_REPORT_POWER_STATUS) {
     68             return handleReportPowerStatus(cmd);
     69         }
     70         return false;
     71     }
     72 
     73     private boolean handleReportPowerStatus(HdmiCecMessage cmd) {
     74         int sourceAddress = cmd.getSource();
     75         int oldStatus = mPowerStatus.get(sourceAddress, INVALID_POWER_STATUS);
     76         if (oldStatus == INVALID_POWER_STATUS) {
     77             // if no device exists for incoming message, hands it over to other actions.
     78             return false;
     79         }
     80         int newStatus = cmd.getParams()[0] & 0xFF;
     81         updatePowerStatus(sourceAddress, newStatus, true);
     82         return true;
     83     }
     84 
     85     @Override
     86     void handleTimerEvent(int state) {
     87         switch (mState) {
     88             case STATE_WAIT_FOR_NEXT_MONITORING:
     89                 queryPowerStatus();
     90                 break;
     91             case STATE_WAIT_FOR_REPORT_POWER_STATUS:
     92                 handleTimeout();
     93                 break;
     94         }
     95     }
     96 
     97     private void handleTimeout() {
     98         for (int i = 0; i < mPowerStatus.size(); ++i) {
     99             int logicalAddress = mPowerStatus.keyAt(i);
    100             updatePowerStatus(logicalAddress, POWER_STATUS_UNKNOWN, false);
    101         }
    102         mPowerStatus.clear();
    103         mState = STATE_WAIT_FOR_NEXT_MONITORING;
    104     }
    105 
    106     private void resetPowerStatus(List<HdmiDeviceInfo> deviceInfos) {
    107         mPowerStatus.clear();
    108         for (HdmiDeviceInfo info : deviceInfos) {
    109             mPowerStatus.append(info.getLogicalAddress(), info.getDevicePowerStatus());
    110         }
    111     }
    112 
    113     private void queryPowerStatus() {
    114         List<HdmiDeviceInfo> deviceInfos = tv().getDeviceInfoList(false);
    115         resetPowerStatus(deviceInfos);
    116         for (HdmiDeviceInfo info : deviceInfos) {
    117             final int logicalAddress = info.getLogicalAddress();
    118             sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(getSourceAddress(),
    119                     logicalAddress),
    120                     new SendMessageCallback() {
    121                         @Override
    122                         public void onSendCompleted(int error) {
    123                             // If fails to send <Give Device Power Status>,
    124                             // update power status into UNKNOWN.
    125                             if (error != SendMessageResult.SUCCESS) {
    126                                updatePowerStatus(logicalAddress, POWER_STATUS_UNKNOWN, true);
    127                             }
    128                         }
    129                     });
    130         }
    131 
    132         mState = STATE_WAIT_FOR_REPORT_POWER_STATUS;
    133 
    134         // Add both timers, monitoring and timeout.
    135         addTimer(STATE_WAIT_FOR_NEXT_MONITORING, MONITIROING_INTERNAL_MS);
    136         addTimer(STATE_WAIT_FOR_REPORT_POWER_STATUS, REPORT_POWER_STATUS_TIMEOUT_MS);
    137     }
    138 
    139     private void updatePowerStatus(int logicalAddress, int newStatus, boolean remove) {
    140         tv().updateDevicePowerStatus(logicalAddress, newStatus);
    141 
    142         if (remove) {
    143             mPowerStatus.delete(logicalAddress);
    144         }
    145     }
    146 }
    147