content: /*
   * 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.util.SparseArray;
  
  /**
   * This class handles the incoming messages when HdmiCecService is in the standby mode.
   */
  public final class HdmiCecStandbyModeHandler {
  
      private interface CecMessageHandler {
          boolean handle(HdmiCecMessage message);
      }
  
      private static final class Bystander implements CecMessageHandler {
          @Override
          public boolean handle(HdmiCecMessage message) {
              return true;
          }
      }
  
      private static final class Bypasser implements CecMessageHandler {
          @Override
          public boolean handle(HdmiCecMessage message) {
              return false;
          }
      }
  
      private final class Aborter implements CecMessageHandler {
          private final int mReason;
          public Aborter(int reason) {
              mReason = reason;
          }
          @Override
          public boolean handle(HdmiCecMessage message) {
              mService.maySendFeatureAbortCommand(message, mReason);
              return true;
          }
      }
  
      private final class AutoOnHandler implements CecMessageHandler {
          @Override
          public boolean handle(HdmiCecMessage message) {
              if (!mTv.getAutoWakeup()) {
                  mAborterRefused.handle(message);
                  return true;
              }
              return false;
          }
      }
  
      private final class UserControlProcessedHandler implements CecMessageHandler {
          @Override
          public boolean handle(HdmiCecMessage message) {
              // The power status here is always standby.
              if (HdmiCecLocalDevice.isPowerOnOrToggleCommand(message)) {
                  return false;
              } else if (HdmiCecLocalDevice.isPowerOffOrToggleCommand(message)) {
                  return true;
              }
              return mAborterIncorrectMode.handle(message);
          }
      }
  
      private final HdmiControlService mService;
      private final HdmiCecLocalDeviceTv mTv;
  
      private final SparseArray<CecMessageHandler> mCecMessageHandlers = new SparseArray<>();
      private final CecMessageHandler mDefaultHandler = new Aborter(
              Constants.ABORT_UNRECOGNIZED_OPCODE);
      private final CecMessageHandler mAborterIncorrectMode = new Aborter(
              Constants.ABORT_NOT_IN_CORRECT_MODE);
      private final CecMessageHandler mAborterRefused = new Aborter(Constants.ABORT_REFUSED);
      private final CecMessageHandler mAutoOnHandler = new AutoOnHandler();
      private final CecMessageHandler mBypasser = new Bypasser();
      private final CecMessageHandler mBystander = new Bystander();
      private final UserControlProcessedHandler
              mUserControlProcessedHandler = new UserControlProcessedHandler();
  
      public HdmiCecStandbyModeHandler(HdmiControlService service, HdmiCecLocalDeviceTv tv) {
          mService = service;
          mTv = tv;
  
          addHandler(Constants.MESSAGE_IMAGE_VIEW_ON, mAutoOnHandler);
          addHandler(Constants.MESSAGE_TEXT_VIEW_ON, mAutoOnHandler);
  
          addHandler(Constants.MESSAGE_ACTIVE_SOURCE, mBystander);
          addHandler(Constants.MESSAGE_REQUEST_ACTIVE_SOURCE, mBystander);
          addHandler(Constants.MESSAGE_ROUTING_CHANGE, mBystander);
          addHandler(Constants.MESSAGE_ROUTING_INFORMATION, mBystander);
          addHandler(Constants.MESSAGE_SET_STREAM_PATH, mBystander);
          addHandler(Constants.MESSAGE_STANDBY, mBystander);
          addHandler(Constants.MESSAGE_SET_MENU_LANGUAGE, mBystander);
          addHandler(Constants.MESSAGE_DEVICE_VENDOR_ID, mBystander);
          addHandler(Constants.MESSAGE_USER_CONTROL_RELEASED, mBystander);
          addHandler(Constants.MESSAGE_REPORT_POWER_STATUS, mBystander);
          addHandler(Constants.MESSAGE_FEATURE_ABORT, mBystander);
          addHandler(Constants.MESSAGE_INACTIVE_SOURCE, mBystander);
          addHandler(Constants.MESSAGE_SYSTEM_AUDIO_MODE_STATUS, mBystander);
          addHandler(Constants.MESSAGE_REPORT_AUDIO_STATUS, mBystander);
  
          // If TV supports the following messages during power-on, ignore them and do nothing,
          // else reply with <Feature Abort>["Unrecognized Opcode"]
          // <Deck Status>, <Tuner Device Status>, <Tuner Cleared Status>, <Timer Status>
          addHandler(Constants.MESSAGE_RECORD_STATUS, mBystander);
  
          // If TV supports the following messages during power-on, reply with <Feature Abort>["Not
          // in correct mode to respond"], else reply with <Feature Abort>["Unrecognized Opcode"]
          // <Give Tuner Device Status>, <Select Digital Service>, <Tuner Step Decrement>,
          // <Tuner Stem Increment>, <Menu Status>.
          addHandler(Constants.MESSAGE_RECORD_TV_SCREEN, mAborterIncorrectMode);
          addHandler(Constants.MESSAGE_INITIATE_ARC, mAborterIncorrectMode);
          addHandler(Constants.MESSAGE_TERMINATE_ARC, mAborterIncorrectMode);
  
          addHandler(Constants.MESSAGE_GIVE_PHYSICAL_ADDRESS, mBypasser);
          addHandler(Constants.MESSAGE_GET_MENU_LANGUAGE, mBypasser);
          addHandler(Constants.MESSAGE_REPORT_PHYSICAL_ADDRESS, mBypasser);
          addHandler(Constants.MESSAGE_GIVE_DEVICE_VENDOR_ID, mBypasser);
          addHandler(Constants.MESSAGE_GIVE_OSD_NAME, mBypasser);
          addHandler(Constants.MESSAGE_SET_OSD_NAME, mBypasser);
  
          addHandler(Constants.MESSAGE_USER_CONTROL_PRESSED, mUserControlProcessedHandler);
  
          addHandler(Constants.MESSAGE_GIVE_DEVICE_POWER_STATUS, mBypasser);
          addHandler(Constants.MESSAGE_ABORT, mBypasser);
          addHandler(Constants.MESSAGE_GET_CEC_VERSION, mBypasser);
  
          addHandler(Constants.MESSAGE_VENDOR_COMMAND_WITH_ID, mAborterIncorrectMode);
          addHandler(Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE, mAborterIncorrectMode);
      }
  
      private void addHandler(int opcode, CecMessageHandler handler) {
          mCecMessageHandlers.put(opcode, handler);
      }
  
      /**
       * Handles the CEC message in the standby mode.
       *
       * @param message {@link HdmiCecMessage} to be processed
       * @return true if the message is handled in the handler, false means that the message is need
       *         to be dispatched to the local device.
       */
      boolean handleCommand(HdmiCecMessage message) {
          CecMessageHandler handler = mCecMessageHandlers.get(message.getOpcode());
          if (handler != null) {
              return handler.handle(message);
          }
          return mDefaultHandler.handle(message);
      }
  }
  