content: /*
   * Copyright (C) 2018 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.wifi;
  
  import java.io.FileDescriptor;
  import java.io.PrintWriter;
  
  /**
   * This class represents the list of SAR inputs that will be used to select the proper
   * power profile.
   * This includes:
   *  - SAR sensor status
   *  - Is there an ongoing voice call
   *  - Is SoftAP active
   * It also contains info about state of the other Wifi modes
   *  - Client mode (Sta)
   *  - ScanOnly mode
   * It also keeps history for the reporting of SAR states/scenario to avoid unnecessary reporting
   *  - keeps track of the last reported states
   *  - keeps track of the last reported SAR scenario
   *  - keeps track of if all wifi modes were disabled (no reporting should happen then)
   */
  public class SarInfo {
      /**
       * This value is used as an initial value for the last reported scenario
       * It is intended to be different than all valid SAR scenario values (including the
       * reset value).
       * Using this to initialize the lastReportedScenario results in that the first scenario
       * (including reset) would be reported.
       */
      public static final int INITIAL_SAR_SCENARIO = -2;
  
      /**
       * This value is used for the reset scenario (no TX Power backoff)
       * Valid scenario values only include scenarios with Tx Power backoff,
       * so we need this one to represent the "No backoff" case.
       */
      public static final int RESET_SAR_SCENARIO = -1;
  
      private static final String SAR_SENSOR_FREE_SPACE_STR = "SAR_SENSOR_FREE_SPACE";
      private static final String SAR_SENSOR_NEAR_BODY_STR  = "SAR_SENSOR_NEAR_BODY";
      private static final String SAR_SENSOR_NEAR_HAND_STR  = "SAR_SENSOR_NEAR_HAND";
      private static final String SAR_SENSOR_NEAR_HEAD_STR  = "SAR_SENSOR_NEAR_HEAD";
  
      public static final int SAR_SENSOR_FREE_SPACE = 1;
      public static final int SAR_SENSOR_NEAR_HAND  = 2;
      public static final int SAR_SENSOR_NEAR_HEAD  = 3;
      public static final int SAR_SENSOR_NEAR_BODY  = 4;
  
      /* For Logging */
      private static final String TAG = "WifiSarInfo";
  
      /* SAR support configs */
      public boolean sarVoiceCallSupported;
      public boolean sarSapSupported;
      public boolean sarSensorSupported;
  
      public int sensorState = SAR_SENSOR_FREE_SPACE;
      public boolean isWifiClientEnabled = false;
      public boolean isWifiSapEnabled = false;
      public boolean isWifiScanOnlyEnabled = false;
      public boolean isVoiceCall = false;
      public boolean isEarPieceActive = false;
      public int attemptedSarScenario = RESET_SAR_SCENARIO;
  
      private boolean mAllWifiDisabled = true;
  
      /* Variables representing the last successfully reported values to hal */
      private int mLastReportedSensorState = SAR_SENSOR_FREE_SPACE;
      private boolean mLastReportedIsWifiSapEnabled = false;
      private boolean mLastReportedIsVoiceCall = false;
      private boolean mLastReportedIsEarPieceActive = false;
      private int mLastReportedScenario = INITIAL_SAR_SCENARIO;
      private long mLastReportedScenarioTs = 0;
  
      /**
       * shouldReport()
       * This method returns false in the following cases:
       * 1. If all Wifi modes are disabled.
       * 2. Values contributing to the SAR scenario selection have not changed
       *    since last successful reporting.
       *
       * Special cases to allow for devices that require setting the SAR scenario value
       * when the chip comes up (initial startup, or during operation)
       * 1. This method would report true even with unchanged values from last reporting,
       *    if any wifi mode is just enabled after all wifi modes were disabled.
       * 2. This method would report true the first time it is called with any wifi mode enabled.
       */
      public boolean shouldReport() {
          /* Check if all Wifi modes are disabled */
          if (!isWifiClientEnabled && !isWifiSapEnabled && !isWifiScanOnlyEnabled) {
              mAllWifiDisabled = true;
              return false;
          }
  
          /* Check if Wifi was all disabled before this call */
          if (mAllWifiDisabled) {
              return true;
          }
  
          /* Check if some change happened since last successful reporting */
          if ((sensorState != mLastReportedSensorState)
                  || (isWifiSapEnabled != mLastReportedIsWifiSapEnabled)
                  || (isVoiceCall != mLastReportedIsVoiceCall)
                  || (isEarPieceActive != mLastReportedIsEarPieceActive)) {
              return true;
          } else {
              return false;
          }
      }
  
      /**
       * reportingSuccessful()
       * This method is called when reporting SAR scenario is fully successful
       * This results in caching the last reported inputs for future comparison.
       */
      public void reportingSuccessful() {
          mLastReportedSensorState = sensorState;
          mLastReportedIsWifiSapEnabled = isWifiSapEnabled;
          mLastReportedIsVoiceCall = isVoiceCall;
          mLastReportedIsEarPieceActive = isEarPieceActive;
          mLastReportedScenario = attemptedSarScenario;
          mLastReportedScenarioTs = System.currentTimeMillis();
  
          mAllWifiDisabled = false;
      }
  
      /**
       *  resetSarScenarioNeeded()
       *  Returns true if a call towards HAL to reset SAR scenario would be necessary.
       *  Returns false if the last call to HAL was already a reset, and hence
       *  another call to reset the SAR scenario would be redundant.
       */
      public boolean resetSarScenarioNeeded() {
          return setSarScenarioNeeded(RESET_SAR_SCENARIO);
      }
  
      /**
       * setSarScenarioNeeded()
       * Returns true if a call towards HAL to set SAR scenario to that value would be
       * necessary. This happens in the following cases:
       *   1. All Wifi modes were disabled, hence we need to init the SAR scenario value.
       *   2. The new scenario is different from the last reported one.
       *
       * Returns false if the last call to HAL was to set the scenario to that value, hence,
       * another call to set the SAR scenario to the same value would be redundant.
       */
      public boolean setSarScenarioNeeded(int scenario) {
          attemptedSarScenario = scenario;
  
          if (mAllWifiDisabled || (mLastReportedScenario != scenario)) {
              return true;
          }
          return false;
      }
  
      /**
       * dump()
       * Dumps the state of SarInfo
       */
      public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
          pw.println("Dump of SarInfo");
          pw.println("Current values:");
          pw.println("    Sensor state is: " + sensorStateToString(sensorState));
          pw.println("    Voice Call state is: " + isVoiceCall);
          pw.println("    Wifi Client state is: " + isWifiClientEnabled);
          pw.println("    Wifi Soft AP state is: " + isWifiSapEnabled);
          pw.println("    Wifi ScanOnly state is: " + isWifiScanOnlyEnabled);
          pw.println("    Earpiece state is : " + isEarPieceActive);
          pw.println("Last reported values:");
          pw.println("    Sensor state is: " + sensorStateToString(mLastReportedSensorState));
          pw.println("    Soft AP state is: " + mLastReportedIsWifiSapEnabled);
          pw.println("    Voice Call state is: " + mLastReportedIsVoiceCall);
          pw.println("    Earpiece state is: " + mLastReportedIsEarPieceActive);
          pw.println("Last reported scenario: " + mLastReportedScenario);
          pw.println("Reported " +  (System.currentTimeMillis() - mLastReportedScenarioTs) / 1000
                  + " seconds ago");
      }
  
      /**
       * Convert SAR sensor state to string
       */
      public static String sensorStateToString(int sensorState) {
          switch(sensorState) {
              case SAR_SENSOR_FREE_SPACE:
                  return SAR_SENSOR_FREE_SPACE_STR;
              case SAR_SENSOR_NEAR_BODY:
                  return SAR_SENSOR_NEAR_BODY_STR;
              case SAR_SENSOR_NEAR_HAND:
                  return SAR_SENSOR_NEAR_HAND_STR;
              case SAR_SENSOR_NEAR_HEAD:
                  return SAR_SENSOR_NEAR_HEAD_STR;
              default:
                  return "Invalid SAR sensor state";
          }
      }
  }
  