diff options
13 files changed, 2221 insertions, 205 deletions
diff --git a/service/java/com/android/server/wifi/SarInfo.java b/service/java/com/android/server/wifi/SarInfo.java new file mode 100644 index 000000000..6eb777c65 --- /dev/null +++ b/service/java/com/android/server/wifi/SarInfo.java @@ -0,0 +1,194 @@ +/* + * 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 body 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"; + + public boolean mSarSensorEnabled; + + public int mSensorState = SAR_SENSOR_FREE_SPACE; + public boolean mIsWifiClientEnabled = false; + public boolean mIsWifiSapEnabled = false; + public boolean mIsWifiScanOnlyEnabled = false; + public boolean mIsVoiceCall = false; + public int mAttemptedSarScenario = 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 int mLastReportedScenario = INITIAL_SAR_SCENARIO; + + SarInfo(boolean sarSensorEnabled) { + mSarSensorEnabled = sarSensorEnabled; + } + + /** + * 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 (!mIsWifiClientEnabled && !mIsWifiSapEnabled && !mIsWifiScanOnlyEnabled) { + 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 ((mSensorState != mLastReportedSensorState) + || (mIsWifiSapEnabled != mLastReportedIsWifiSapEnabled) + || (mIsVoiceCall != mLastReportedIsVoiceCall)) { + 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 = mSensorState; + mLastReportedIsWifiSapEnabled = mIsWifiSapEnabled; + mLastReportedIsVoiceCall = mIsVoiceCall; + mLastReportedScenario = mAttemptedSarScenario; + + 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. + * 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) { + mAttemptedSarScenario = scenario; + return (mLastReportedScenario != scenario); + } + + /** + * dump() + * Dumps the state of SarInfo + */ + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println("*** WiFi SAR Info Dump ***"); + pw.println("Current values:"); + pw.println(" Sensor state is: " + sensorStateToString(mSensorState)); + pw.println(" Voice Call state is: " + mIsVoiceCall); + pw.println(" Wifi Client state is: " + mIsWifiClientEnabled); + pw.println(" Wifi Soft AP state is: " + mIsWifiSapEnabled); + pw.println(" Wifi ScanOnly state is: " + mIsWifiScanOnlyEnabled); + 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); + } + + /** + * 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"; + } + } +} diff --git a/service/java/com/android/server/wifi/SarManager.java b/service/java/com/android/server/wifi/SarManager.java index da48a8537..d38eab981 100644 --- a/service/java/com/android/server/wifi/SarManager.java +++ b/service/java/com/android/server/wifi/SarManager.java @@ -20,14 +20,16 @@ import static android.telephony.TelephonyManager.CALL_STATE_IDLE; import static android.telephony.TelephonyManager.CALL_STATE_OFFHOOK; import static android.telephony.TelephonyManager.CALL_STATE_RINGING; -import android.content.BroadcastReceiver; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; import android.net.wifi.WifiManager; import android.os.Looper; import android.telephony.PhoneStateListener; import android.telephony.TelephonyManager; +import android.text.TextUtils; import android.util.Log; import com.android.internal.R; @@ -40,24 +42,28 @@ import java.util.List; * This class provides the Support for SAR to control WiFi TX power limits. * It deals with the following: * - Tracking the STA state through calls from the ClientModeManager. + * - Tracking the SAP state through calls from SoftApManager + * - Tracking the Scan-Only state through ScanOnlyModeManager * - Tracking the state of the Cellular calls or data. - * - Based on above, selecting the SAR profile to use and programming it in wifi hal. + * - Tracking the sensor indicating proximity to user head/hand/body. + * - It constructs the sar info and send it towards the HAL */ public class SarManager { - /* For Logging */ private static final String TAG = "WifiSarManager"; private boolean mVerboseLoggingEnabled = true; + private SarInfo mSarInfo; + /* Configuration for SAR */ private boolean mEnableSarTxPowerLimit; + private boolean mEnableSarBodyProximity; + /* Sensor event definitions */ + private int mSarSensorEventFreeSpace; + private int mSarSensorEventNearBody; + private int mSarSensorEventNearHand; + private int mSarSensorEventNearHead; - /* Current SAR Scenario */ - private int mCurrentSarScenario = WifiNative.TX_POWER_SCENARIO_NORMAL; - - /* Booleans for Cell and wifi states */ - private boolean mCellOn = false; - private boolean mWifiStaEnabled = false; /** * Other parameters passed in or created in the constructor. */ @@ -65,6 +71,8 @@ public class SarManager { private final TelephonyManager mTelephonyManager; private final WifiPhoneStateListener mPhoneStateListener; private final WifiNative mWifiNative; + private final SarSensorEventListener mSensorListener; + private final SensorManager mSensorManager; private final Looper mLooper; /** @@ -73,30 +81,157 @@ public class SarManager { SarManager(Context context, TelephonyManager telephonyManager, Looper looper, - WifiNative wifiNative) { + WifiNative wifiNative, + SensorManager sensorManager) { mContext = context; mTelephonyManager = telephonyManager; mWifiNative = wifiNative; mLooper = looper; + mSensorManager = sensorManager; mPhoneStateListener = new WifiPhoneStateListener(looper); + mSensorListener = new SarSensorEventListener(); + + readSarConfigs(); + if (mEnableSarTxPowerLimit) { + mSarInfo = new SarInfo(mEnableSarBodyProximity); + registerListeners(); + } + } + + private void readSarConfigs() { + mEnableSarTxPowerLimit = mContext.getResources().getBoolean( + R.bool.config_wifi_framework_enable_sar_tx_power_limit); + /* In case SAR is disabled, + then SAR sensor is automatically disabled as well (irrespective of the config) */ + if (!mEnableSarTxPowerLimit) { + mEnableSarBodyProximity = false; + return; + } + + mEnableSarBodyProximity = mContext.getResources().getBoolean( + R.bool.config_wifi_framework_enable_body_proximity_sar_tx_power_limit); + + /* Read the sar sensor event Ids */ + if (mEnableSarBodyProximity) { + mSarSensorEventFreeSpace = mContext.getResources().getInteger( + R.integer.config_wifi_framework_sar_free_space_event_id); + mSarSensorEventNearBody = mContext.getResources().getInteger( + R.integer.config_wifi_framework_sar_near_body_event_id); + mSarSensorEventNearHand = mContext.getResources().getInteger( + R.integer.config_wifi_framework_sar_near_hand_event_id); + mSarSensorEventNearHead = mContext.getResources().getInteger( + R.integer.config_wifi_framework_sar_near_head_event_id); + } + } - registerListeners(); + private void registerListeners() { + /* Listen for Phone State changes */ + registerPhoneStateListener(); + + /* Only listen for SAR sensor if supported */ + if (mEnableSarBodyProximity) { + /* Register the SAR sensor listener. + * If this fails, we will assume worst case (near head) */ + if (!registerSensorListener()) { + Log.e(TAG, "Failed to register sensor listener, setting Sensor to NearHead"); + /*TODO Need to add a metric to determine how often this happens */ + mSarInfo.mSensorState = SarInfo.SAR_SENSOR_NEAR_HEAD; + } + } } /** - * Starts the SAR Manager by initializing the different listeners + * Register the phone state listener. */ - private void registerListeners() { - /* First read the configuration for SAR Support */ - mEnableSarTxPowerLimit = mContext.getResources().getBoolean( - R.bool.config_wifi_framework_enable_voice_call_sar_tx_power_limit); + private void registerPhoneStateListener() { + Log.i(TAG, "Registering for telephony call state changes"); + mTelephonyManager.listen( + mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE); + } - /* Only Start listening for events if SAR is enabled */ - if (mEnableSarTxPowerLimit) { - Log.d(TAG, "Registering Listeners for the SAR Manager"); + /** + * Register the body/hand/head proximity sensor. + */ + private boolean registerSensorListener() { + Log.i(TAG, "Registering for Sensor notification Listener"); + return mSensorListener.register(); + } + + /** + * Update Wifi Client State + */ + public void setClientWifiState(int state) { + boolean newIsEnabled; + /* No action is taken if SAR is not enabled */ + if (!mEnableSarTxPowerLimit) { + return; + } + + if (state == WifiManager.WIFI_STATE_DISABLED) { + newIsEnabled = false; + } else if (state == WifiManager.WIFI_STATE_ENABLED) { + newIsEnabled = true; + } else { + /* No change so exiting with no action */ + return; + } + + /* Report change to HAL if needed */ + if (mSarInfo.mIsWifiClientEnabled != newIsEnabled) { + mSarInfo.mIsWifiClientEnabled = newIsEnabled; + updateSarScenario(); + } + } + + /** + * Update Wifi SoftAP State + */ + public void setSapWifiState(int state) { + boolean newIsEnabled; + /* No action is taken if SAR is not enabled */ + if (!mEnableSarTxPowerLimit) { + return; + } + + if (state == WifiManager.WIFI_AP_STATE_DISABLED) { + newIsEnabled = false; + } else if (state == WifiManager.WIFI_AP_STATE_ENABLED) { + newIsEnabled = true; + } else { + /* No change so exiting with no action */ + return; + } + + /* Report change to HAL if needed */ + if (mSarInfo.mIsWifiSapEnabled != newIsEnabled) { + mSarInfo.mIsWifiSapEnabled = newIsEnabled; + updateSarScenario(); + } + } - /* Listen for Phone State changes */ - registerPhoneListener(); + /** + * Update Wifi ScanOnly State + */ + public void setScanOnlyWifiState(int state) { + boolean newIsEnabled; + /* No action is taken if SAR is not enabled */ + if (!mEnableSarTxPowerLimit) { + return; + } + + if (state == WifiManager.WIFI_STATE_DISABLED) { + newIsEnabled = false; + } else if (state == WifiManager.WIFI_STATE_ENABLED) { + newIsEnabled = true; + } else { + /* No change so exiting with no action */ + return; + } + + /* Report change to HAL if needed */ + if (mSarInfo.mIsWifiScanOnlyEnabled != newIsEnabled) { + mSarInfo.mIsWifiScanOnlyEnabled = newIsEnabled; + updateSarScenario(); } } @@ -104,42 +239,52 @@ public class SarManager { * Report Cell state event */ private void onCellStateChangeEvent(int state) { - boolean currentCellOn = mCellOn; - + boolean newIsVoiceCall; switch (state) { case CALL_STATE_OFFHOOK: case CALL_STATE_RINGING: - mCellOn = true; + newIsVoiceCall = true; break; case CALL_STATE_IDLE: - mCellOn = false; + newIsVoiceCall = false; break; default: Log.e(TAG, "Invalid Cell State: " + state); + return; } - if (mCellOn != currentCellOn) { + /* Report change to HAL if needed */ + if (mSarInfo.mIsVoiceCall != newIsVoiceCall) { + mSarInfo.mIsVoiceCall = newIsVoiceCall; updateSarScenario(); } } /** - * Update Wifi Client State + * Report an event from the SAR sensor */ - public void setClientWifiState(int state) { - /* No action is taken if SAR is not enabled */ - if (!mEnableSarTxPowerLimit) return; - - if (state == WifiManager.WIFI_STATE_DISABLED && mWifiStaEnabled) { - mWifiStaEnabled = false; - } else if (state == WifiManager.WIFI_STATE_ENABLED && !mWifiStaEnabled) { - mWifiStaEnabled = true; + private void onSarSensorEvent(int sarSensorEvent) { + int newSensorState; + if (sarSensorEvent == mSarSensorEventFreeSpace) { + newSensorState = SarInfo.SAR_SENSOR_FREE_SPACE; + } else if (sarSensorEvent == mSarSensorEventNearBody) { + newSensorState = SarInfo.SAR_SENSOR_NEAR_BODY; + } else if (sarSensorEvent == mSarSensorEventNearHand) { + newSensorState = SarInfo.SAR_SENSOR_NEAR_HAND; + } else if (sarSensorEvent == mSarSensorEventNearHead) { + newSensorState = SarInfo.SAR_SENSOR_NEAR_HEAD; + } else { + Log.e(TAG, "Invalid SAR sensor event id: " + sarSensorEvent); + return; + } - /* Since no wifi interface was up, - time for SAR scenario to take effect */ - sendTxPowerScenario(mCurrentSarScenario); + /* Report change to HAL if needed */ + if (mSarInfo.mSensorState != newSensorState) { + Log.d(TAG, "Setting Sensor state to " + SarInfo.sensorStateToString(newSensorState)); + mSarInfo.mSensorState = newSensorState; + updateSarScenario(); } } @@ -147,7 +292,6 @@ public class SarManager { * Enable/disable verbose logging. */ public void enableVerboseLogging(int verbose) { - Log.d(TAG, "Inside enableVerboseLogging: " + verbose); if (verbose > 0) { mVerboseLoggingEnabled = true; } else { @@ -155,18 +299,16 @@ public class SarManager { } } - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println("*** WiFi SAR Manager Dump ***"); - pw.println("Current SAR Scenario is " + scenarioToString(mCurrentSarScenario)); - } - /** - * Register the phone listener. + * dump() + * Dumps SarManager state (as well as its SarInfo member variable state) */ - private void registerPhoneListener() { - Log.i(TAG, "Registering for telephony call state changes"); - mTelephonyManager.listen( - mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE); + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println("*** WiFi SAR Manager Dump ***"); + pw.println("isSarEnabled: " + mEnableSarTxPowerLimit); + pw.println("isSarSensorEnabled: " + mEnableSarBodyProximity); + pw.println(""); + mSarInfo.dump(fd, pw, args); } /** @@ -177,69 +319,96 @@ public class SarManager { super(looper); } + /** + * onCallStateChanged() + * This callback is called when a SAR sensor event is received + * Note that this runs in the WifiStateMachineHandlerThread + * since the corresponding Looper was passed to the WifiPhoneStateListener constructor. + */ @Override public void onCallStateChanged(int state, String incomingNumber) { Log.d(TAG, "Received Phone State Change: " + state); /* In case of an unsolicited event */ - if (!mEnableSarTxPowerLimit) return; - + if (!mEnableSarTxPowerLimit) { + return; + } onCellStateChangeEvent(state); } } - /** - * update the Current SAR Scenario based on factors including: - * - Do we have an ongoing cellular voice call. - */ - private void updateSarScenario() { - int newSarScenario; + private class SarSensorEventListener implements SensorEventListener { - if (mCellOn) { - newSarScenario = WifiNative.TX_POWER_SCENARIO_VOICE_CALL; - } else { - newSarScenario = WifiNative.TX_POWER_SCENARIO_NORMAL; - } + private Sensor mSensor; - if (newSarScenario != mCurrentSarScenario) { + /** + * Register the SAR listener to get SAR sensor events + */ + private boolean register() { + /* Get the sensor type from configuration */ + String sensorType = mContext.getResources().getString( + R.string.config_wifi_sar_sensor_type); + if (TextUtils.isEmpty(sensorType)) { + Log.e(TAG, "Empty SAR sensor type"); + return false; + } + + /* Get the sensor object */ + Sensor sensor = null; + List<Sensor> sensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL); + for (Sensor s : sensorList) { + if (sensorType.equals(s.getStringType())) { + sensor = s; + break; + } + } + if (sensor == null) { + Log.e(TAG, "Failed to Find the SAR Sensor"); + return false; + } - // Only update HAL with new scenario if WiFi interface is enabled - if (mWifiStaEnabled) { - Log.d(TAG, "Sending SAR Scenario #" + scenarioToString(newSarScenario)); - sendTxPowerScenario(newSarScenario); + /* Now register the listener */ + if (!mSensorManager.registerListener(this, sensor, + SensorManager.SENSOR_DELAY_NORMAL)) { + Log.e(TAG, "Failed to register SAR Sensor Listener"); + return false; } - mCurrentSarScenario = newSarScenario; + return true; } - } - /** - * sendTxPowerScenario() - * Update HAL with the new power scenario. - */ - private void sendTxPowerScenario(int newSarScenario) { - if (!mWifiNative.selectTxPowerScenario(newSarScenario)) { - Log.e(TAG, "Failed to set TX power scenario"); + /** + * onSensorChanged() + * This callback is called when a SAR sensor event is received + * Note that this runs in the WifiStateMachineHandlerThread + * since, the corresponding Looper was passed to the SensorManager instance. + */ + @Override + public void onSensorChanged(SensorEvent event) { + onSarSensorEvent((int) event.values[0]); + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { } } /** - * Convert SAR Scenario to string + * updateSarScenario() + * Update HAL with the new SAR scenario if needed. */ - private String scenarioToString(int scenario) { - String str; - switch(scenario) { - case WifiNative.TX_POWER_SCENARIO_NORMAL: - str = "TX_POWER_SCENARIO_NORMAL"; - break; - case WifiNative.TX_POWER_SCENARIO_VOICE_CALL: - str = "TX_POWER_SCENARIO_VOICE_CALL"; - break; - default: - str = "Invalid Scenario"; - break; + private void updateSarScenario() { + if (!mSarInfo.shouldReport()) { + return; + } + + /* Report info to HAL*/ + if (mWifiNative.selectTxPowerScenario(mSarInfo)) { + mSarInfo.reportingSuccessful(); + } else { + Log.e(TAG, "Failed in WifiNative.selectTxPowerScenario()"); } - return str; + return; } } diff --git a/service/java/com/android/server/wifi/ScanOnlyModeManager.java b/service/java/com/android/server/wifi/ScanOnlyModeManager.java index 346d2ca67..991657929 100644 --- a/service/java/com/android/server/wifi/ScanOnlyModeManager.java +++ b/service/java/com/android/server/wifi/ScanOnlyModeManager.java @@ -48,6 +48,7 @@ public class ScanOnlyModeManager implements ActiveModeManager { private final Listener mListener; private final ScanRequestProxy mScanRequestProxy; private final WakeupController mWakeupController; + private final SarManager mSarManager; private String mClientInterfaceName; private boolean mIfaceIsUp = false; @@ -58,13 +59,15 @@ public class ScanOnlyModeManager implements ActiveModeManager { @NonNull WifiNative wifiNative, @NonNull Listener listener, @NonNull WifiMetrics wifiMetrics, @NonNull ScanRequestProxy scanRequestProxy, - @NonNull WakeupController wakeupController) { + @NonNull WakeupController wakeupController, + @NonNull SarManager sarManager) { mContext = context; mWifiNative = wifiNative; mListener = listener; mWifiMetrics = wifiMetrics; mScanRequestProxy = scanRequestProxy; mWakeupController = wakeupController; + mSarManager = sarManager; mStateMachine = new ScanOnlyModeStateMachine(looper); } @@ -242,6 +245,7 @@ public class ScanOnlyModeManager implements ActiveModeManager { mIfaceIsUp = false; onUpChanged(mWifiNative.isInterfaceUp(mClientInterfaceName)); + mSarManager.setScanOnlyWifiState(WifiManager.WIFI_STATE_ENABLED); } @Override @@ -281,6 +285,7 @@ public class ScanOnlyModeManager implements ActiveModeManager { mClientInterfaceName = null; } updateWifiState(WifiManager.WIFI_STATE_DISABLED); + mSarManager.setScanOnlyWifiState(WifiManager.WIFI_STATE_DISABLED); // once we leave started, nothing else to do... stop the state machine mStateMachine.quitNow(); diff --git a/service/java/com/android/server/wifi/SoftApManager.java b/service/java/com/android/server/wifi/SoftApManager.java index 6c52918a5..1afe9ed9d 100644 --- a/service/java/com/android/server/wifi/SoftApManager.java +++ b/service/java/com/android/server/wifi/SoftApManager.java @@ -93,6 +93,8 @@ public class SoftApManager implements ActiveModeManager { private int mNumAssociatedStations = 0; private boolean mTimeoutEnabled = false; + private final SarManager mSarManager; + /** * Listener for soft AP events. */ @@ -118,7 +120,8 @@ public class SoftApManager implements ActiveModeManager { @NonNull WifiManager.SoftApCallback callback, @NonNull WifiApConfigStore wifiApConfigStore, @NonNull SoftApModeConfiguration apConfig, - @NonNull WifiMetrics wifiMetrics) { + @NonNull WifiMetrics wifiMetrics, + @NonNull SarManager sarManager) { mContext = context; mFrameworkFacade = framework; mWifiNative = wifiNative; @@ -133,6 +136,7 @@ public class SoftApManager implements ActiveModeManager { mApConfig = config; } mWifiMetrics = wifiMetrics; + mSarManager = sarManager; mStateMachine = new SoftApStateMachine(looper); } @@ -491,6 +495,9 @@ public class SoftApManager implements ActiveModeManager { if (mSettingObserver != null) { mSettingObserver.register(); } + + mSarManager.setSapWifiState(WifiManager.WIFI_AP_STATE_ENABLED); + Log.d(TAG, "Resetting num stations on start"); mNumAssociatedStations = 0; scheduleTimeoutMessage(); @@ -512,6 +519,8 @@ public class SoftApManager implements ActiveModeManager { mWifiMetrics.addSoftApUpChangedEvent(false, mMode); updateApState(WifiManager.WIFI_AP_STATE_DISABLED, WifiManager.WIFI_AP_STATE_DISABLING, 0); + + mSarManager.setSapWifiState(WifiManager.WIFI_AP_STATE_DISABLED); mApInterfaceName = null; mIfaceIsUp = false; mStateMachine.quitNow(); diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java index 0e30af841..199548090 100644 --- a/service/java/com/android/server/wifi/WifiInjector.java +++ b/service/java/com/android/server/wifi/WifiInjector.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.app.ActivityManager; import android.app.AppOpsManager; import android.content.Context; +import android.hardware.SystemSensorManager; import android.net.NetworkKey; import android.net.NetworkScoreManager; import android.net.wifi.IWifiScanner; @@ -253,7 +254,7 @@ public class WifiInjector { this, mWifiConfigManager, mWifiPermissionsUtil, mWifiMetrics, mClock); mSarManager = new SarManager(mContext, makeTelephonyManager(), wifiStateMachineLooper, - mWifiNative); + mWifiNative, new SystemSensorManager(mContext, wifiStateMachineLooper)); if (mUseRealLogger) { mWifiDiagnostics = new WifiDiagnostics( mContext, this, mWifiNative, mBuildProperties, @@ -464,7 +465,7 @@ public class WifiInjector { @NonNull SoftApModeConfiguration config) { return new SoftApManager(mContext, mWifiStateMachineHandlerThread.getLooper(), mFrameworkFacade, mWifiNative, mCountryCode.getCountryCode(), callback, - mWifiApConfigStore, config, mWifiMetrics); + mWifiApConfigStore, config, mWifiMetrics, mSarManager); } /** @@ -476,7 +477,8 @@ public class WifiInjector { public ScanOnlyModeManager makeScanOnlyModeManager( @NonNull ScanOnlyModeManager.Listener listener) { return new ScanOnlyModeManager(mContext, mWifiStateMachineHandlerThread.getLooper(), - mWifiNative, listener, mWifiMetrics, mScanRequestProxy, mWakeupController); + mWifiNative, listener, mWifiMetrics, mScanRequestProxy, mWakeupController, + mSarManager); } /** diff --git a/service/java/com/android/server/wifi/WifiNative.java b/service/java/com/android/server/wifi/WifiNative.java index 0f785873b..bc599c141 100644 --- a/service/java/com/android/server/wifi/WifiNative.java +++ b/service/java/com/android/server/wifi/WifiNative.java @@ -2820,21 +2820,14 @@ public class WifiNative { } /** - * Tx power level scenarios that can be selected. - */ - public static final int TX_POWER_SCENARIO_NORMAL = 0; - public static final int TX_POWER_SCENARIO_VOICE_CALL = 1; - - /** - * Select one of the pre-configured TX power level scenarios or reset it back to normal. - * Primarily used for meeting SAR requirements during voice calls. + * Select one of the pre-configured transmit power level scenarios or reset it back to normal. + * Primarily used for meeting SAR requirements. * - * @param scenario Should be one {@link #TX_POWER_SCENARIO_NORMAL} or - * {@link #TX_POWER_SCENARIO_VOICE_CALL}. + * @param sarInfo The collection of inputs used to select the SAR scenario. * @return true for success; false for failure or if the HAL version does not support this API. */ - public boolean selectTxPowerScenario(int scenario) { - return mWifiVendorHal.selectTxPowerScenario(scenario); + public boolean selectTxPowerScenario(SarInfo sarInfo) { + return mWifiVendorHal.selectTxPowerScenario(sarInfo); } /******************************************************** diff --git a/service/java/com/android/server/wifi/WifiVendorHal.java b/service/java/com/android/server/wifi/WifiVendorHal.java index 0d73459b5..361387e2f 100644 --- a/service/java/com/android/server/wifi/WifiVendorHal.java +++ b/service/java/com/android/server/wifi/WifiVendorHal.java @@ -2653,13 +2653,95 @@ public class WifiVendorHal { return android.hardware.wifi.V1_2.IWifiStaIface.castFrom(iface); } + /** + * sarPowerBackoffRequired_1_1() + * This method checks if we need to backoff wifi Tx power due to SAR requirements. + * It handles the case when the device is running the V1_1 version of WifiChip HAL + * In that HAL version, it is required to perform wifi Tx power backoff only if + * a voice call is ongoing. + */ + private boolean sarPowerBackoffRequired_1_1(SarInfo sarInfo) { + /* As long as no voice call is active, no backoff is needed */ + return sarInfo.mIsVoiceCall; + } + + /** + * frameworkToHalTxPowerScenario_1_1() + * This method maps the information inside the SarInfo instance into a SAR scenario + * when device is running the V1_1 version of WifiChip HAL. + * In this HAL version, only one scenario is defined which is for VOICE_CALL + * otherwise, an exception is thrown. + */ + private int frameworkToHalTxPowerScenario_1_1(SarInfo sarInfo) { + if (sarInfo.mIsVoiceCall) { + return android.hardware.wifi.V1_1.IWifiChip.TxPowerScenario.VOICE_CALL; + } else { + throw new IllegalArgumentException("bad scenario: voice call not active"); + } + } - private int frameworkToHalTxPowerScenario(int scenario) { - switch (scenario) { - case WifiNative.TX_POWER_SCENARIO_VOICE_CALL: + /** + * sarPowerBackoffRequired_1_2() + * This method checks if we need to backoff wifi Tx power due to SAR requirements. + * It handles the case when the device is running the V1_2 version of WifiChip HAL + * In that HAL version, behavior depends on if SAR sensor input is considered in this device. + * If it is, then whenever the device is near the user body/hand/head, back-off is required. + * Otherwise, we should revert to the V1_1 HAL behavior which is only to perform backoff when + * a voice call is ongoing. + */ + private boolean sarPowerBackoffRequired_1_2(SarInfo sarInfo) { + if (sarInfo.mSarSensorEnabled) { + return (sarInfo.mSensorState != SarInfo.SAR_SENSOR_FREE_SPACE); + } else { + return sarInfo.mIsVoiceCall; + } + } + + /** + * frameworkToHalTxPowerScenario_1_2() + * This method maps the information inside the SarInfo instance into a SAR scenario + * when device is running the V1_2 version of WifiChip HAL. + * In this HAL version, behavior depends on if SAR sensor input is considered in this device. + * If it is, then based on regulatory compliance requirements, + * - There is no need to treat NEAR_HAND different from NEAR_BODY, both can be considered + * near the user body. + * - Running in softAP mode can be treated the same way as running a voice call from tx power + * backoff perspective. + * If SAR sensor input is not considered in this device, then we should revert to the V1_1 HAL + * behavior, and the only valid scenario would be when a voice call is ongoing. + */ + private int frameworkToHalTxPowerScenario_1_2(SarInfo sarInfo) { + if (sarInfo.mSarSensorEnabled) { + switch(sarInfo.mSensorState) { + case SarInfo.SAR_SENSOR_NEAR_BODY: + case SarInfo.SAR_SENSOR_NEAR_HAND: + if (sarInfo.mIsVoiceCall || sarInfo.mIsWifiSapEnabled) { + return android.hardware.wifi.V1_2.IWifiChip + .TxPowerScenario.ON_BODY_CELL_ON; + } else { + return android.hardware.wifi.V1_2.IWifiChip + .TxPowerScenario.ON_BODY_CELL_OFF; + } + + case SarInfo.SAR_SENSOR_NEAR_HEAD: + if (sarInfo.mIsVoiceCall || sarInfo.mIsWifiSapEnabled) { + return android.hardware.wifi.V1_2.IWifiChip + .TxPowerScenario.ON_HEAD_CELL_ON; + } else { + return android.hardware.wifi.V1_2.IWifiChip + .TxPowerScenario.ON_HEAD_CELL_OFF; + } + + default: + throw new IllegalArgumentException("bad scenario: Invalid sensor state"); + } + } else { + /* SAR Sensors not enabled, act like V1_1 */ + if (sarInfo.mIsVoiceCall) { return android.hardware.wifi.V1_1.IWifiChip.TxPowerScenario.VOICE_CALL; - default: - throw new IllegalArgumentException("bad scenario: " + scenario); + } else { + throw new IllegalArgumentException("bad scenario: voice call not active"); + } } } @@ -2667,35 +2749,130 @@ public class WifiVendorHal { * Select one of the pre-configured TX power level scenarios or reset it back to normal. * Primarily used for meeting SAR requirements during voice calls. * - * @param scenario Should be one {@link WifiNative#TX_POWER_SCENARIO_NORMAL} or - * {@link WifiNative#TX_POWER_SCENARIO_VOICE_CALL}. + * Note: If it was found out that the scenario to be reported is the same as last reported one, + * then exit with success. + * This is to handle the case when some HAL versions deal with different inputs equally, + * in that case, we should not call the hal unless there is a change in scenario. + * Note: It is assumed that this method is only called if SAR is enabled. The logic of whether + * to call it or not resides in SarManager class. + * Note: This method is called whether SAR sensor is supported or not. The passed SarInfo object + * contains a flag to indicate the SAR sensor support. + * + * @param sarInfo The collection of inputs to select the SAR scenario. * @return true for success; false for failure or if the HAL version does not support this API. */ - public boolean selectTxPowerScenario(int scenario) { + public boolean selectTxPowerScenario(SarInfo sarInfo) { synchronized (sLock) { - try { - android.hardware.wifi.V1_1.IWifiChip iWifiChipV11 = getWifiChipForV1_1Mockable(); - if (iWifiChipV11 == null) return boolResult(false); - WifiStatus status; - if (scenario != WifiNative.TX_POWER_SCENARIO_NORMAL) { - int halScenario; - try { - halScenario = frameworkToHalTxPowerScenario(scenario); - } catch (IllegalArgumentException e) { - mLog.err("Illegal argument for select tx power scenario") - .c(e.toString()).flush(); + // First attempt to get a V_1_2 instance of the Wifi HAL. + android.hardware.wifi.V1_2.IWifiChip iWifiChipV12 = getWifiChipForV1_2Mockable(); + if (iWifiChipV12 != null) { + return selectTxPowerScenario_1_2(iWifiChipV12, sarInfo); + } + + // Now attempt to get a V_1_1 instance of the Wifi HAL. + android.hardware.wifi.V1_1.IWifiChip iWifiChipV11 = getWifiChipForV1_1Mockable(); + if (iWifiChipV11 != null) { + return selectTxPowerScenario_1_1(iWifiChipV11, sarInfo); + } + + // HAL version does not support SAR + return false; + } + } + + private boolean selectTxPowerScenario_1_1( + android.hardware.wifi.V1_1.IWifiChip iWifiChip, SarInfo sarInfo) { + WifiStatus status; + try { + if (sarPowerBackoffRequired_1_1(sarInfo)) { + // Power backoff is needed, so calculate the required scenario, + // and attempt to set it. + int halScenario = frameworkToHalTxPowerScenario_1_1(sarInfo); + if (sarInfo.setSarScenarioNeeded(halScenario)) { + status = iWifiChip.selectTxPowerScenario(halScenario); + if (ok(status)) { + mLog.e("Setting SAR scenario to " + halScenario); + return true; + } else { + mLog.e("Failed to set SAR scenario to " + halScenario); return false; } - status = iWifiChipV11.selectTxPowerScenario(halScenario); + } + + // Reaching here means setting SAR scenario would be redundant, + // do nothing and return with success. + return true; + } + + // We don't need to perform power backoff, so attempt to reset SAR scenario. + if (sarInfo.resetSarScenarioNeeded()) { + status = iWifiChip.resetTxPowerScenario(); + if (ok(status)) { + mLog.d("Resetting SAR scenario"); + return true; } else { - status = iWifiChipV11.resetTxPowerScenario(); + mLog.e("Failed to reset SAR scenario"); + return false; } - if (!ok(status)) return false; - } catch (RemoteException e) { - handleRemoteException(e); - return false; } + + // Resetting SAR scenario would be redundant, + // do nothing and return with success. return true; + } catch (RemoteException e) { + handleRemoteException(e); + return false; + } catch (IllegalArgumentException e) { + mLog.err("Illegal argument for selectTxPowerScenario_1_1()").c(e.toString()).flush(); + return false; + } + } + + private boolean selectTxPowerScenario_1_2( + android.hardware.wifi.V1_2.IWifiChip iWifiChip, SarInfo sarInfo) { + WifiStatus status; + try { + if (sarPowerBackoffRequired_1_2(sarInfo)) { + // Power backoff is needed, so calculate the required scenario, + // and attempt to set it. + int halScenario = frameworkToHalTxPowerScenario_1_2(sarInfo); + if (sarInfo.setSarScenarioNeeded(halScenario)) { + status = iWifiChip.selectTxPowerScenario_1_2(halScenario); + if (ok(status)) { + mLog.e("Setting SAR scenario to " + halScenario); + return true; + } else { + mLog.e("Failed to set SAR scenario to " + halScenario); + return false; + } + } + + // Reaching here means setting SAR scenario would be redundant, + // do nothing and return with success. + return true; + } + + // We don't need to perform power backoff, so attempt to reset SAR scenario. + if (sarInfo.resetSarScenarioNeeded()) { + status = iWifiChip.resetTxPowerScenario(); + if (ok(status)) { + mLog.d("Resetting SAR scenario"); + return true; + } else { + mLog.e("Failed to reset SAR scenario"); + return false; + } + } + + // Resetting SAR scenario would be redundant, + // do nothing and return with success. + return true; + } catch (RemoteException e) { + handleRemoteException(e); + return false; + } catch (IllegalArgumentException e) { + mLog.err("Illegal argument for selectTxPowerScenario_1_2()").c(e.toString()).flush(); + return false; } } diff --git a/tests/wifitests/src/com/android/server/wifi/SarInfoTest.java b/tests/wifitests/src/com/android/server/wifi/SarInfoTest.java new file mode 100644 index 000000000..1f0e44c8a --- /dev/null +++ b/tests/wifitests/src/com/android/server/wifi/SarInfoTest.java @@ -0,0 +1,287 @@ +/* + * 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 static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.support.test.filters.SmallTest; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +/** + * unit tests for {@link com.android.server.wifi.SarInfo}. + */ +@SmallTest +public class SarInfoTest { + private static final String TAG = "WifiSarInfoTest"; + + private SarInfo mSarInfo; + + private static final int SAR_SCENARIO_1 = 1; + private static final int SAR_SCENARIO_2 = 2; + + @Before + public void setUp() throws Exception { + mSarInfo = new SarInfo(true); + } + + @After + public void cleanUp() throws Exception { + } + + /** + * Test that at start, resetSarScenarioNeeded returns true, + * to allow for initial setting of normal scenario. + */ + @Test + public void testSarInfo_resetSarScenarioNeed_atStart() throws Exception { + assertTrue(mSarInfo.resetSarScenarioNeeded()); + } + + /** + * Test that at start, setSarScenarioNeeded returns true. + */ + @Test + public void testSarInfo_setSarScenarioNeeded_atStart() throws Exception { + assertTrue(mSarInfo.setSarScenarioNeeded(SAR_SCENARIO_1)); + } + + /** + * Test performing two successive reset of SAR scenario. + * The first should succeed, while the second should fail, since it is redundant. + */ + @Test + public void testSarInfo_repeat_reset_scenario() throws Exception { + /* Initial reset is allowed */ + assertTrue(mSarInfo.resetSarScenarioNeeded()); + mSarInfo.reportingSuccessful(); + + /* Now resetting again should not be allowed */ + assertFalse(mSarInfo.resetSarScenarioNeeded()); + } + + /** + * Test performing set SAR scenario after reset. + * The two attempts should succeed. + */ + @Test + public void testSarInfo_set_after_reset_scenario() throws Exception { + assertTrue(mSarInfo.resetSarScenarioNeeded()); + mSarInfo.reportingSuccessful(); + + /* Setting scenario should be allowed, since last call was for a reset */ + assertTrue(mSarInfo.setSarScenarioNeeded(SAR_SCENARIO_1)); + } + + /** + * Test performing setting SAR scenario twice with same value. + * The second attempt should fail. + */ + @Test + public void testSarInfo_set_twice_same_value_scenario() throws Exception { + assertTrue(mSarInfo.setSarScenarioNeeded(SAR_SCENARIO_1)); + mSarInfo.reportingSuccessful(); + + /* Second attempt should fail */ + assertFalse(mSarInfo.setSarScenarioNeeded(SAR_SCENARIO_1)); + } + + /** + * Test performing setting SAR scenario twice with different values. + * Both attempts should succeed. + */ + @Test + public void testSarInfo_set_twice_different_values_scenario() throws Exception { + assertTrue(mSarInfo.setSarScenarioNeeded(SAR_SCENARIO_1)); + mSarInfo.reportingSuccessful(); + + /* Setting scenario should be allowed */ + assertTrue(mSarInfo.setSarScenarioNeeded(SAR_SCENARIO_2)); + } + + /** + * Test performing reset of SAR scenario after setting it. + * Both attempts should succeed. + */ + @Test + public void testSarInfo_reset_after_set_scenario() throws Exception { + assertTrue(mSarInfo.setSarScenarioNeeded(SAR_SCENARIO_1)); + mSarInfo.reportingSuccessful(); + + /* Resetting scenario should be allowed */ + assertTrue(mSarInfo.resetSarScenarioNeeded()); + } + + /** + * Test that at start, shouldReport returns false (wifi modes still disabled). + */ + @Test + public void testSarInfo_shouldReport_all_wifi_disabled() throws Exception { + assertFalse(mSarInfo.shouldReport()); + } + + /** + * Test that once Wifi (any mode) is enabled, shouldReport returns true. + */ + @Test + public void testSarInfo_shouldReport_wifi_enabled() throws Exception { + mSarInfo.mIsWifiClientEnabled = true; + assertTrue(mSarInfo.shouldReport()); + } + + /** + * Test that setting sensor (with wifi disabled), shouldReport returns false. + */ + @Test + public void testSarInfo_check_sensor_wifi_disabled() throws Exception { + mSarInfo.mSensorState = SarInfo.SAR_SENSOR_NEAR_HEAD; + assertFalse(mSarInfo.shouldReport()); + } + + /** + * Test that setting sensor (with some wifi mode enabled), shouldReport returns true. + */ + @Test + public void testSarInfo_check_sensor_wifi_enabled() throws Exception { + mSarInfo.mIsWifiSapEnabled = true; + assertTrue(mSarInfo.shouldReport()); + mSarInfo.reportingSuccessful(); + + mSarInfo.mSensorState = SarInfo.SAR_SENSOR_NEAR_HEAD; + assertTrue(mSarInfo.shouldReport()); + } + + /** + * Test that setting sensor (with some wifi mode enabled), shouldReport returns true + * only the first time, following attempts should return false (since sensor state + * did not change) + */ + @Test + public void testSarInfo_check_sensor_multiple_wifi_enabled() throws Exception { + mSarInfo.mIsWifiScanOnlyEnabled = true; + mSarInfo.mSensorState = SarInfo.SAR_SENSOR_NEAR_HEAD; + assertTrue(mSarInfo.shouldReport()); + mSarInfo.reportingSuccessful(); + + assertFalse(mSarInfo.shouldReport()); + } + + /** + * Test that setting sensor with different values (with wifi enabled), + * shouldReport returns true every time. + */ + @Test + public void testSarInfo_check_sensor_multiple_values_wifi_enabled() throws Exception { + mSarInfo.mIsWifiClientEnabled = true; + mSarInfo.mSensorState = SarInfo.SAR_SENSOR_NEAR_HEAD; + assertTrue(mSarInfo.shouldReport()); + mSarInfo.reportingSuccessful(); + + mSarInfo.mSensorState = SarInfo.SAR_SENSOR_NEAR_BODY; + assertTrue(mSarInfo.shouldReport()); + mSarInfo.reportingSuccessful(); + + mSarInfo.mSensorState = SarInfo.SAR_SENSOR_NEAR_HEAD; + assertTrue(mSarInfo.shouldReport()); + } + + /** + * Test setting sensor while wifi is disabled, then enable wifi. + */ + @Test + public void testSarInfo_change_sensors_while_wifi_disabled() throws Exception { + mSarInfo.mSensorState = SarInfo.SAR_SENSOR_NEAR_HEAD; + assertFalse(mSarInfo.shouldReport()); + + mSarInfo.mIsWifiClientEnabled = true; + assertTrue(mSarInfo.shouldReport()); + mSarInfo.reportingSuccessful(); + } + + /** + * Test having a voice call, shouldReport should return true + * Note: will need to report once before starting the call to remove + * the effect of sensor state change. + */ + @Test + public void testSarInfo_voice_call_wifi_enabled() throws Exception { + mSarInfo.mIsWifiClientEnabled = true; + assertTrue(mSarInfo.shouldReport()); + mSarInfo.reportingSuccessful(); + + mSarInfo.mIsVoiceCall = true; + assertTrue(mSarInfo.shouldReport()); + } + + /** + * Test starting SAP, shouldReport should return true + * Note: will need to report once before starting SAP to remove + * the effect of sensor state change. + */ + @Test + public void testSarInfo_sap_wifi_enabled() throws Exception { + mSarInfo.mIsWifiClientEnabled = true; + assertTrue(mSarInfo.shouldReport()); + mSarInfo.reportingSuccessful(); + + mSarInfo.mIsWifiSapEnabled = true; + assertTrue(mSarInfo.shouldReport()); + } + + /** + * Test that setting sensor (with wifi enabled), reporting not successful + * Then, we should expect that shouldReport returns true evne if we have + * no further changes until reporting is successful. + */ + @Test + public void testSarInfo_check_sensor_reporting_no_success_reporting() throws Exception { + mSarInfo.mIsWifiClientEnabled = true; + mSarInfo.mSensorState = SarInfo.SAR_SENSOR_NEAR_HEAD; + assertTrue(mSarInfo.shouldReport()); + + /* No call to reportingSuccessful() will be done */ + assertTrue(mSarInfo.shouldReport()); + + /* Now call reportingSuccessful() */ + mSarInfo.reportingSuccessful(); + assertFalse(mSarInfo.shouldReport()); + } + + /** + * Test that setting sensor (with wifi enabled), reporting successful + * Then, changing the sensor state with no successful reporting. + * Followed by reverting to the previous state. + */ + @Test + public void testSarInfo_check_sensor_reporting_no_success_reporting_revert() throws Exception { + mSarInfo.mIsWifiClientEnabled = true; + mSarInfo.mSensorState = SarInfo.SAR_SENSOR_NEAR_HEAD; + assertTrue(mSarInfo.shouldReport()); + mSarInfo.reportingSuccessful(); + + /* Changing the sensor state and fail to report */ + mSarInfo.mSensorState = SarInfo.SAR_SENSOR_NEAR_BODY; + assertTrue(mSarInfo.shouldReport()); + + /* Changing the sensor back to the same value as last reported */ + mSarInfo.mSensorState = SarInfo.SAR_SENSOR_NEAR_HEAD; + assertFalse(mSarInfo.shouldReport()); + } +} diff --git a/tests/wifitests/src/com/android/server/wifi/SarManagerTest.java b/tests/wifitests/src/com/android/server/wifi/SarManagerTest.java index 163280a39..369dcba33 100644 --- a/tests/wifitests/src/com/android/server/wifi/SarManagerTest.java +++ b/tests/wifitests/src/com/android/server/wifi/SarManagerTest.java @@ -16,39 +16,44 @@ package com.android.server.wifi; +import static android.telephony.TelephonyManager.CALL_STATE_IDLE; +import static android.telephony.TelephonyManager.CALL_STATE_OFFHOOK; + import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.Mockito.*; -import static android.telephony.TelephonyManager.CALL_STATE_IDLE; -import static android.telephony.TelephonyManager.CALL_STATE_OFFHOOK; - -import android.app.test.MockAnswerUtil.AnswerWithArguments; -import android.content.BroadcastReceiver; import android.content.Context; -import android.content.IntentFilter; import android.content.pm.ApplicationInfo; +import android.hardware.Sensor; import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SystemSensorManager; import android.net.wifi.WifiManager; import android.os.Build; import android.os.test.TestLooper; import android.support.test.filters.SmallTest; import android.telephony.PhoneStateListener; import android.telephony.TelephonyManager; -import android.util.Log; import com.android.internal.R; import org.junit.After; import org.junit.Before; import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; import org.mockito.ArgumentCaptor; import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + /** * unit tests for {@link com.android.server.wifi.SarManager}. */ @@ -56,6 +61,12 @@ import org.mockito.MockitoAnnotations; public class SarManagerTest { private static final String TAG = "WifiSarManagerTest"; private static final String OP_PACKAGE_NAME = "com.xxx"; + private static final String SAR_SENSOR_NAME = "com.google.sensor.sar"; + + private static final int SAR_SENSOR_EVENT_FREE_SPACE = 1; + private static final int SAR_SENSOR_EVENT_HAND = 2; + private static final int SAR_SENSOR_EVENT_HEAD = 3; + private static final int SAR_SENSOR_EVENT_BODY = 4; private void enableDebugLogs() { mSarMgr.enableVerboseLogging(1); @@ -70,23 +81,26 @@ public class SarManagerTest { private TestLooper mLooper; private MockResources mResources; private PhoneStateListener mPhoneStateListener; + private List<Sensor> mSensorList; + private Sensor mSensor; + private SarInfo mSarInfo; - @Mock private Context mContext; + @Mock private Context mContext; + @Mock SensorEventListener mSensorEventListener; + @Mock SystemSensorManager mSensorManager; @Mock TelephonyManager mTelephonyManager; @Mock private ApplicationInfo mMockApplInfo; @Mock WifiNative mWifiNative; @Before public void setUp() throws Exception { - Log.e(TAG, "Setting Up ..."); - - // Ensure Looper exists + /* Ensure Looper exists */ mLooper = new TestLooper(); MockitoAnnotations.initMocks(this); /* Default behavior is to return with success */ - when(mWifiNative.selectTxPowerScenario(anyInt())).thenReturn(true); + when(mWifiNative.selectTxPowerScenario(any(SarInfo.class))).thenReturn(true); mResources = getMockResources(); @@ -105,15 +119,78 @@ public class SarManagerTest { } /** + * Helper function to capture SarInfo object + */ + private void captureSarInfo(WifiNative wifiNative) { + /* Capture the SensorEventListener */ + ArgumentCaptor<SarInfo> sarInfoCaptor = ArgumentCaptor.forClass(SarInfo.class); + verify(wifiNative).selectTxPowerScenario(sarInfoCaptor.capture()); + mSarInfo = sarInfoCaptor.getValue(); + assertNotNull(mSarInfo); + } + + /** + * Helper function to create and prepare sensor info + */ + private void prepareSensorInfo(boolean registerReturn) { + /* Create a sensor object (note, this can not be mocked since it is a final class) */ + Constructor<Sensor> constructor = + (Constructor<Sensor>) Sensor.class.getDeclaredConstructors()[0]; + constructor.setAccessible(true); + + try { + mSensor = constructor.newInstance(); + } catch (Exception e) { + fail("Failed to create a sensor object"); + } + + /* Now set the mStringType field with the proper field */ + Field declaredField = null; + try { + declaredField = Sensor.class.getDeclaredField("mStringType"); + declaredField.setAccessible(true); + declaredField.set(mSensor, SAR_SENSOR_NAME); + } catch (Exception e) { + fail("Could not set sensor string type"); + } + + /* Prepare the sensor list */ + mSensorList = new ArrayList<Sensor>(); + mSensorList.add(mSensor); + when(mSensorManager.getSensorList(Sensor.TYPE_ALL)).thenReturn(mSensorList); + when(mSensorManager.registerListener(any(SensorEventListener.class), any(Sensor.class), + anyInt())).thenReturn(registerReturn); + } + + /** * Helper function to set configuration for SAR and create the SAR Manager * */ - private void createSarManager(boolean isSarEnabled) { + private void createSarManager(boolean isSarEnabled, boolean isSarSensorEnabled) { + mResources.setBoolean( + R.bool.config_wifi_framework_enable_sar_tx_power_limit, isSarEnabled); mResources.setBoolean( - R.bool.config_wifi_framework_enable_voice_call_sar_tx_power_limit, isSarEnabled); + R.bool.config_wifi_framework_enable_body_proximity_sar_tx_power_limit, + isSarSensorEnabled); + mResources.setString(R.string.config_wifi_sar_sensor_type, SAR_SENSOR_NAME); + + /* Set the event id configs */ + mResources.setInteger(R.integer.config_wifi_framework_sar_free_space_event_id, + SAR_SENSOR_EVENT_FREE_SPACE); + mResources.setInteger(R.integer.config_wifi_framework_sar_near_hand_event_id, + SAR_SENSOR_EVENT_HAND); + mResources.setInteger(R.integer.config_wifi_framework_sar_near_head_event_id, + SAR_SENSOR_EVENT_HEAD); + mResources.setInteger(R.integer.config_wifi_framework_sar_near_body_event_id, + SAR_SENSOR_EVENT_BODY); + + /* Prepare sensor info only if SarSensorEnabled */ + if (isSarSensorEnabled) { + prepareSensorInfo(true); + } mSarMgr = new SarManager(mContext, mTelephonyManager, mLooper.getLooper(), - mWifiNative); + mWifiNative, mSensorManager); if (isSarEnabled) { /* Capture the PhoneStateListener */ @@ -122,6 +199,17 @@ public class SarManagerTest { verify(mTelephonyManager).listen(phoneStateListenerCaptor.capture(), eq(PhoneStateListener.LISTEN_CALL_STATE)); mPhoneStateListener = phoneStateListenerCaptor.getValue(); + assertNotNull(mPhoneStateListener); + } + + if (isSarSensorEnabled) { + /* Capture the SensorEventListener */ + ArgumentCaptor<SensorEventListener> sensorEventListenerCaptor = + ArgumentCaptor.forClass(SensorEventListener.class); + verify(mSensorManager).registerListener(sensorEventListenerCaptor.capture(), + any(Sensor.class), anyInt()); + mSensorEventListener = sensorEventListenerCaptor.getValue(); + assertNotNull(mSensorEventListener); } /* Enable logs from SarManager */ @@ -129,12 +217,70 @@ public class SarManagerTest { } /** + * Helper function to create SarManager with some error cases for sensor handling + */ + private void createSarManagerSensorNegTest(String configSensorName, boolean addToConfigs, + boolean sensorRegisterReturn) { + mResources.setBoolean( + R.bool.config_wifi_framework_enable_sar_tx_power_limit, true); + mResources.setBoolean( + R.bool.config_wifi_framework_enable_body_proximity_sar_tx_power_limit, true); + if (addToConfigs) { + mResources.setString(R.string.config_wifi_sar_sensor_type, configSensorName); + } + + /* Set the event id configs */ + mResources.setInteger(R.integer.config_wifi_framework_sar_free_space_event_id, + SAR_SENSOR_EVENT_FREE_SPACE); + mResources.setInteger(R.integer.config_wifi_framework_sar_near_hand_event_id, + SAR_SENSOR_EVENT_HAND); + mResources.setInteger(R.integer.config_wifi_framework_sar_near_head_event_id, + SAR_SENSOR_EVENT_HEAD); + mResources.setInteger(R.integer.config_wifi_framework_sar_near_body_event_id, + SAR_SENSOR_EVENT_BODY); + + prepareSensorInfo(sensorRegisterReturn); + + mSarMgr = new SarManager(mContext, mTelephonyManager, mLooper.getLooper(), + mWifiNative, mSensorManager); + + /* Capture the PhoneStateListener */ + ArgumentCaptor<PhoneStateListener> phoneStateListenerCaptor = + ArgumentCaptor.forClass(PhoneStateListener.class); + verify(mTelephonyManager).listen(phoneStateListenerCaptor.capture(), + eq(PhoneStateListener.LISTEN_CALL_STATE)); + mPhoneStateListener = phoneStateListenerCaptor.getValue(); + assertNotNull(mPhoneStateListener); + + /* Enable logs from SarManager */ + enableDebugLogs(); + } + + /** + * Helper function to create and pass a sensor event + */ + private void sendSensorEvent(int eventId) { + SensorEvent event; + Constructor<SensorEvent> constructor = + (Constructor<SensorEvent>) SensorEvent.class.getDeclaredConstructors()[0]; + constructor.setAccessible(true); + + try { + event = constructor.newInstance(1); + event.values[0] = (float) eventId; + mSensorEventListener.onSensorChanged(event); + } catch (Exception e) { + fail("Failed to create a Sensor Event"); + } + } + + /** * Test that we do register the telephony call state listener on devices which do support * setting/resetting Tx power limit. */ @Test public void testSarMgr_enabledTxPowerScenario_registerPhone() throws Exception { - createSarManager(true); + createSarManager(true, false); verify(mTelephonyManager).listen(any(), eq(PhoneStateListener.LISTEN_CALL_STATE)); } @@ -144,7 +290,7 @@ public class SarManagerTest { */ @Test public void testSarMgr_disabledTxPowerScenario_registerPhone() throws Exception { - createSarManager(false); + createSarManager(false, false); verify(mTelephonyManager, never()).listen(any(), anyInt()); } @@ -153,27 +299,24 @@ public class SarManagerTest { * Tx power scenario upon receiving {@link TelephonyManager#CALL_STATE_OFFHOOK} when WiFi STA * is enabled * In this case Wifi is enabled first, then off-hook is detected - * Expectation is to get {@link WifiNative#TX_POWER_SCENARIO_NORMAL} when WiFi is turned on - * followed by {@link WifiNative#TX_POWER_SCENARIO_VOICE_CALL} when OFFHOOK event is detected */ @Test public void testSarMgr_enabledTxPowerScenario_wifiOn_offHook() throws Exception { - createSarManager(true); - assertNotNull(mPhoneStateListener); + createSarManager(true, false); InOrder inOrder = inOrder(mWifiNative); /* Enable WiFi State */ mSarMgr.setClientWifiState(WifiManager.WIFI_STATE_ENABLED); + captureSarInfo(mWifiNative); - inOrder.verify(mWifiNative).selectTxPowerScenario( - eq(WifiNative.TX_POWER_SCENARIO_NORMAL)); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertFalse(mSarInfo.mIsVoiceCall); /* Set phone state to OFFHOOK */ mPhoneStateListener.onCallStateChanged(CALL_STATE_OFFHOOK, ""); - - inOrder.verify(mWifiNative).selectTxPowerScenario( - eq(WifiNative.TX_POWER_SCENARIO_VOICE_CALL)); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertTrue(mSarInfo.mIsVoiceCall); } /** @@ -181,66 +324,70 @@ public class SarManagerTest { * Tx power scenario upon receiving {@link TelephonyManager#CALL_STATE_OFFHOOK} when WiFi STA * is enabled * In this case off-hook event is detected first, then wifi is turned on - * Expectation is to get {@link WifiNative#TX_POWER_SCENARIO_VOICE_CALL} once wifi is turned on */ @Test public void testSarMgr_enabledTxPowerScenario_offHook_wifiOn() throws Exception { - createSarManager(true); - assertNotNull(mPhoneStateListener); + createSarManager(true, false); + + InOrder inOrder = inOrder(mWifiNative); /* Set phone state to OFFHOOK */ mPhoneStateListener.onCallStateChanged(CALL_STATE_OFFHOOK, ""); /* Enable WiFi State */ mSarMgr.setClientWifiState(WifiManager.WIFI_STATE_ENABLED); + captureSarInfo(mWifiNative); - verify(mWifiNative).selectTxPowerScenario( - eq(WifiNative.TX_POWER_SCENARIO_VOICE_CALL)); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertTrue(mSarInfo.mIsVoiceCall); } /** * Test that for devices that support setting/resetting Tx Power limits, device sets the proper * Tx power scenarios upon receiving {@link TelephonyManager#CALL_STATE_OFFHOOK} and - * {@link TelephonyManager#CALL_STATE_OFFHOOK} when WiFi STA is enabled + * {@link TelephonyManager#CALL_STATE_IDLE} when WiFi STA is enabled */ @Test public void testSarMgr_enabledTxPowerScenario_wifiOn_offHook_onHook() throws Exception { - createSarManager(true); - assertNotNull(mPhoneStateListener); + createSarManager(true, false); InOrder inOrder = inOrder(mWifiNative); /* Enable WiFi State */ mSarMgr.setClientWifiState(WifiManager.WIFI_STATE_ENABLED); + captureSarInfo(mWifiNative); /* Now device should set tx power scenario to NORMAL */ - inOrder.verify(mWifiNative).selectTxPowerScenario( - eq(WifiNative.TX_POWER_SCENARIO_NORMAL)); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertFalse(mSarInfo.mIsVoiceCall); /* Set phone state to OFFHOOK */ mPhoneStateListener.onCallStateChanged(CALL_STATE_OFFHOOK, ""); /* Device should set tx power scenario to Voice call */ - inOrder.verify(mWifiNative).selectTxPowerScenario( - eq(WifiNative.TX_POWER_SCENARIO_VOICE_CALL)); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertTrue(mSarInfo.mIsVoiceCall); /* Set state back to ONHOOK */ mPhoneStateListener.onCallStateChanged(CALL_STATE_IDLE, ""); /* Device should set tx power scenario to NORMAL again */ - inOrder.verify(mWifiNative).selectTxPowerScenario( - eq(WifiNative.TX_POWER_SCENARIO_NORMAL)); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertFalse(mSarInfo.mIsVoiceCall); + + /* Disable WiFi State */ + mSarMgr.setClientWifiState(WifiManager.WIFI_STATE_DISABLED); + inOrder.verify(mWifiNative, never()).selectTxPowerScenario(any(SarInfo.class)); } /** * Test that for devices that support setting/resetting Tx Power limits, device does not * sets the Tx power scenarios upon receiving {@link TelephonyManager#CALL_STATE_OFFHOOK} and - * {@link TelephonyManager#CALL_STATE_OFFHOOK} when WiFi STA is disabled + * {@link TelephonyManager#CALL_STATE_IDLE} when WiFi STA is disabled */ @Test public void testSarMgr_enabledTxPowerScenario_wifiOff_offHook_onHook() throws Exception { - createSarManager(true); - assertNotNull(mPhoneStateListener); + createSarManager(true, false); InOrder inOrder = inOrder(mWifiNative); @@ -251,6 +398,634 @@ public class SarManagerTest { mPhoneStateListener.onCallStateChanged(CALL_STATE_IDLE, ""); /* Device should not set tx power scenario at all */ - inOrder.verify(mWifiNative, never()).selectTxPowerScenario(anyInt()); + inOrder.verify(mWifiNative, never()).selectTxPowerScenario(any(SarInfo.class)); + } + + /** + * Test that for devices supporting SAR the following scenario: + * - Wifi enabled + * - A call starts + * - Wifi disabled + * - Call ends + * - Wifi back on + */ + @Test + public void testSarMgr_enabledSar_wifiOn_offHook_wifiOff_onHook() throws Exception { + createSarManager(true, false); + + InOrder inOrder = inOrder(mWifiNative); + + /* Enable WiFi State */ + mSarMgr.setClientWifiState(WifiManager.WIFI_STATE_ENABLED); + captureSarInfo(mWifiNative); + + /* Now device should set tx power scenario to NORMAL */ + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertFalse(mSarInfo.mIsVoiceCall); + + /* Set phone state to OFFHOOK */ + mPhoneStateListener.onCallStateChanged(CALL_STATE_OFFHOOK, ""); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertTrue(mSarInfo.mIsVoiceCall); + + /* Disable WiFi State */ + mSarMgr.setClientWifiState(WifiManager.WIFI_STATE_DISABLED); + inOrder.verify(mWifiNative, never()).selectTxPowerScenario(any(SarInfo.class)); + + /* Set state back to ONHOOK */ + mPhoneStateListener.onCallStateChanged(CALL_STATE_IDLE, ""); + inOrder.verify(mWifiNative, never()).selectTxPowerScenario(any(SarInfo.class)); + + /* Enable WiFi State again */ + mSarMgr.setClientWifiState(WifiManager.WIFI_STATE_ENABLED); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertFalse(mSarInfo.mIsVoiceCall); + } + + /** + * Test that for devices supporting SAR, Wifi disabled, a call starts, a call ends, Wifi + * enabled. + */ + @Test + public void testSarMgr_enabledSar_wifiOff_offHook_onHook_wifiOn() throws Exception { + createSarManager(true, false); + + InOrder inOrder = inOrder(mWifiNative); + + /* Set phone state to OFFHOOK */ + mPhoneStateListener.onCallStateChanged(CALL_STATE_OFFHOOK, ""); + inOrder.verify(mWifiNative, never()).selectTxPowerScenario(any(SarInfo.class)); + + /* Set state back to ONHOOK */ + mPhoneStateListener.onCallStateChanged(CALL_STATE_IDLE, ""); + inOrder.verify(mWifiNative, never()).selectTxPowerScenario(any(SarInfo.class)); + + /* Enable WiFi State */ + mSarMgr.setClientWifiState(WifiManager.WIFI_STATE_ENABLED); + captureSarInfo(mWifiNative); + + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertFalse(mSarInfo.mIsVoiceCall); + } + + /** + * Test that for devices supporting SAR, Wifi disabled, a call starts, wifi on, wifi off, + * call ends. + */ + @Test + public void testSarMgr_enabledSar_offHook_wifiOnOff_onHook() throws Exception { + createSarManager(true, false); + + InOrder inOrder = inOrder(mWifiNative); + + /* Set phone state to OFFHOOK */ + mPhoneStateListener.onCallStateChanged(CALL_STATE_OFFHOOK, ""); + inOrder.verify(mWifiNative, never()).selectTxPowerScenario(any(SarInfo.class)); + + /* Enable WiFi State */ + mSarMgr.setClientWifiState(WifiManager.WIFI_STATE_ENABLED); + captureSarInfo(mWifiNative); + assertNotNull(mSarInfo); + + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertTrue(mSarInfo.mIsVoiceCall); + + /* Disable WiFi State */ + mSarMgr.setClientWifiState(WifiManager.WIFI_STATE_DISABLED); + inOrder.verify(mWifiNative, never()).selectTxPowerScenario(any(SarInfo.class)); + + /* Set state back to ONHOOK */ + mPhoneStateListener.onCallStateChanged(CALL_STATE_IDLE, ""); + inOrder.verify(mWifiNative, never()).selectTxPowerScenario(any(SarInfo.class)); + } + + /** + * Test the error case for for devices supporting SAR, Wifi enabled, a call starts, + * call ends. With all of these cases, the call to set Tx power scenario fails. + */ + @Test + public void testSarMgr_enabledSar_error_wifiOn_offOnHook() throws Exception { + createSarManager(true, false); + + when(mWifiNative.selectTxPowerScenario(any(SarInfo.class))).thenReturn(false); + InOrder inOrder = inOrder(mWifiNative); + + /* Enable WiFi State */ + mSarMgr.setClientWifiState(WifiManager.WIFI_STATE_ENABLED); + captureSarInfo(mWifiNative); + assertNotNull(mSarInfo); + + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertFalse(mSarInfo.mIsVoiceCall); + + /* Set phone state to OFFHOOK */ + mPhoneStateListener.onCallStateChanged(CALL_STATE_OFFHOOK, ""); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertTrue(mSarInfo.mIsVoiceCall); + + /* Set state back to ONHOOK */ + mPhoneStateListener.onCallStateChanged(CALL_STATE_IDLE, ""); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertFalse(mSarInfo.mIsVoiceCall); + } + + /** + * Test that for a device that has SAR enabled, with sar sensor enabled, + * wifi enabled, Then Tx power scenarios follow events from sensor for body/hand/head/none + */ + @Test + public void testSarMgr_sarSensorOn_WifiOn_sensorEventsTriggered() throws Exception { + createSarManager(true, true); + + InOrder inOrder = inOrder(mWifiNative); + + /* Enable Wifi Client */ + mSarMgr.setClientWifiState(WifiManager.WIFI_STATE_ENABLED); + captureSarInfo(mWifiNative); + + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_FREE_SPACE, mSarInfo.mSensorState); + + /* Sensor event */ + sendSensorEvent(SAR_SENSOR_EVENT_BODY); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_BODY, mSarInfo.mSensorState); + assertFalse(mSarInfo.mIsVoiceCall); + + /* Sensor event */ + sendSensorEvent(SAR_SENSOR_EVENT_HEAD); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HEAD, mSarInfo.mSensorState); + assertFalse(mSarInfo.mIsVoiceCall); + + /* Sensor event */ + sendSensorEvent(SAR_SENSOR_EVENT_HAND); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HAND, mSarInfo.mSensorState); + assertFalse(mSarInfo.mIsVoiceCall); + + /* Sensor event */ + sendSensorEvent(SAR_SENSOR_EVENT_FREE_SPACE); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_FREE_SPACE, mSarInfo.mSensorState); + assertFalse(mSarInfo.mIsVoiceCall); + } + + /** + * Test that for a device that has SAR enabled, with sar sensor enabled, + * wifi enabled, cellOn, + * then Tx power scenarios follow events from sensor for body/hand/head/none + */ + @Test + public void testSarMgr_sarSensorOn_wifiOn_cellOn_sensorEventsTriggered() throws Exception { + createSarManager(true, true); + + InOrder inOrder = inOrder(mWifiNative); + + /* Enable Wifi Client */ + mSarMgr.setClientWifiState(WifiManager.WIFI_STATE_ENABLED); + captureSarInfo(mWifiNative); + + /* Should get the an event with no calls */ + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_FREE_SPACE, mSarInfo.mSensorState); + assertFalse(mSarInfo.mIsVoiceCall); + + /* Start a Cell call */ + mPhoneStateListener.onCallStateChanged(CALL_STATE_OFFHOOK, ""); + inOrder.verify(mWifiNative).selectTxPowerScenario(any(SarInfo.class)); + assertEquals(SarInfo.SAR_SENSOR_FREE_SPACE, mSarInfo.mSensorState); + assertTrue(mSarInfo.mIsVoiceCall); + + /* Sensor event */ + sendSensorEvent(SAR_SENSOR_EVENT_BODY); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_BODY, mSarInfo.mSensorState); + assertTrue(mSarInfo.mIsVoiceCall); + + /* Sensor event */ + sendSensorEvent(SAR_SENSOR_EVENT_HEAD); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HEAD, mSarInfo.mSensorState); + assertTrue(mSarInfo.mIsVoiceCall); + + /* Sensor event */ + sendSensorEvent(SAR_SENSOR_EVENT_HAND); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HAND, mSarInfo.mSensorState); + assertTrue(mSarInfo.mIsVoiceCall); + + /* Sensor event */ + sendSensorEvent(SAR_SENSOR_EVENT_FREE_SPACE); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_FREE_SPACE, mSarInfo.mSensorState); + assertTrue(mSarInfo.mIsVoiceCall); + } + + /** + * Test that for a device that has SAR enabled, with sar sensor enabled, + * wifi enabled, device next to user head, a call has started and stopped, + * then Tx power scenarios should adjust properly + */ + @Test + public void testSarMgr_sarSensorOn_wifiOn_onHead_cellOnOff() throws Exception { + createSarManager(true, true); + + InOrder inOrder = inOrder(mWifiNative); + + /* Enable Wifi Client */ + mSarMgr.setClientWifiState(WifiManager.WIFI_STATE_ENABLED); + captureSarInfo(mWifiNative); + + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_FREE_SPACE, mSarInfo.mSensorState); + assertFalse(mSarInfo.mIsVoiceCall); + + /* Sensor event */ + sendSensorEvent(SAR_SENSOR_EVENT_HEAD); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HEAD, mSarInfo.mSensorState); + assertFalse(mSarInfo.mIsVoiceCall); + + /* Start a Cell call */ + mPhoneStateListener.onCallStateChanged(CALL_STATE_OFFHOOK, ""); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HEAD, mSarInfo.mSensorState); + assertTrue(mSarInfo.mIsVoiceCall); + + /* End a Cell call */ + mPhoneStateListener.onCallStateChanged(CALL_STATE_IDLE, ""); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HEAD, mSarInfo.mSensorState); + assertFalse(mSarInfo.mIsVoiceCall); + } + + /** + * Test that for a device that has SAR enabled, with sar sensor enabled, + * all wifi states disabled, when a sensor event is triggered no setting of Tx power scenario + * is initiated. + * Then when Wifi is enabled, Tx power setting will be initiated to reflect the sensor event. + */ + @Test + public void testSarMgr_sarSensorOn_WifiOffOn_sensorEventTriggered() throws Exception { + createSarManager(true, true); + + InOrder inOrder = inOrder(mWifiNative); + + /* Sensor event */ + sendSensorEvent(SAR_SENSOR_EVENT_BODY); + inOrder.verify(mWifiNative, never()).selectTxPowerScenario(any(SarInfo.class)); + + /* Enable Wifi Client */ + mSarMgr.setClientWifiState(WifiManager.WIFI_STATE_ENABLED); + captureSarInfo(mWifiNative); + + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_BODY, mSarInfo.mSensorState); + assertFalse(mSarInfo.mIsVoiceCall); + } + + /** + * Test the error case when SAR sensor name does not exist in configuration. + * In this case, SarManager should assume operation near head all the time. + */ + @Test + public void testSarMgr_error_sar_name_does_not_exist() throws Exception { + createSarManagerSensorNegTest(SAR_SENSOR_NAME, false, true); + + InOrder inOrder = inOrder(mWifiNative); + + verify(mSensorManager, never()).registerListener(any(SensorEventListener.class), + any(Sensor.class), anyInt()); + + /* Enable WiFi Client */ + mSarMgr.setClientWifiState(WifiManager.WIFI_STATE_ENABLED); + captureSarInfo(mWifiNative); + + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HEAD, mSarInfo.mSensorState); + assertFalse(mSarInfo.mIsVoiceCall); + + /* Start a Cell Call */ + mPhoneStateListener.onCallStateChanged(CALL_STATE_OFFHOOK, ""); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HEAD, mSarInfo.mSensorState); + assertTrue(mSarInfo.mIsVoiceCall); + + /* End the call */ + mPhoneStateListener.onCallStateChanged(CALL_STATE_IDLE, ""); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HEAD, mSarInfo.mSensorState); + assertFalse(mSarInfo.mIsVoiceCall); + } + + /** + * Test the error case when SarManager uses the wrong sensor name in configuration. + * In this case, SarManager should assume operation near head all the time. + */ + @Test + public void testSarMgr_error_sar_name_mismatch() throws Exception { + createSarManagerSensorNegTest("wrong.sensor.name", true, true); + + InOrder inOrder = inOrder(mWifiNative); + + verify(mSensorManager, never()).registerListener(any(SensorEventListener.class), + any(Sensor.class), anyInt()); + + /* Enable WiFi Client */ + mSarMgr.setClientWifiState(WifiManager.WIFI_STATE_ENABLED); + captureSarInfo(mWifiNative); + + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HEAD, mSarInfo.mSensorState); + assertFalse(mSarInfo.mIsVoiceCall); + + /* Start a Cell Call */ + mPhoneStateListener.onCallStateChanged(CALL_STATE_OFFHOOK, ""); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HEAD, mSarInfo.mSensorState); + assertTrue(mSarInfo.mIsVoiceCall); + + /* End the call */ + mPhoneStateListener.onCallStateChanged(CALL_STATE_IDLE, ""); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HEAD, mSarInfo.mSensorState); + assertFalse(mSarInfo.mIsVoiceCall); + } + + /** + * Test the error case when SarManager fails to register as a SensorEventListener. + * In this case, SarManager should assume operation near head all the time. + */ + @Test + public void testSarMgr_error_sar_register_failure() throws Exception { + createSarManagerSensorNegTest(SAR_SENSOR_NAME, true, false); + + verify(mSensorManager).registerListener(any(SensorEventListener.class), + any(Sensor.class), anyInt()); + + InOrder inOrder = inOrder(mWifiNative); + + /* Enable WiFi Client */ + mSarMgr.setClientWifiState(WifiManager.WIFI_STATE_ENABLED); + captureSarInfo(mWifiNative); + + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HEAD, mSarInfo.mSensorState); + assertFalse(mSarInfo.mIsVoiceCall); + + /* Start a Cell Call */ + mPhoneStateListener.onCallStateChanged(CALL_STATE_OFFHOOK, ""); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HEAD, mSarInfo.mSensorState); + assertTrue(mSarInfo.mIsVoiceCall); + + /* End the call */ + mPhoneStateListener.onCallStateChanged(CALL_STATE_IDLE, ""); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HEAD, mSarInfo.mSensorState); + assertFalse(mSarInfo.mIsVoiceCall); + } + + /** + * Test that Start of SoftAP for a device that does not have SAR enabled does not result in + * setting the Tx power scenario + */ + @Test + public void testSarMgr_disabledTxPowerScenario_sapOn() throws Exception { + createSarManager(false, false); + + /* Enable WiFi SoftAP State */ + mSarMgr.setSapWifiState(WifiManager.WIFI_AP_STATE_ENABLED); + + verify(mWifiNative, never()).selectTxPowerScenario(any(SarInfo.class)); + } + + /** + * Test that Start of SoftAP for a device that has SAR enabled, SAR sensor disabled. + */ + @Test + public void testSarMgr_enabledTxPowerScenario_sapOn() throws Exception { + createSarManager(true, false); + + InOrder inOrder = inOrder(mWifiNative); + + /* Enable WiFi SoftAP State */ + mSarMgr.setSapWifiState(WifiManager.WIFI_AP_STATE_ENABLED); + captureSarInfo(mWifiNative); + + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertFalse(mSarInfo.mIsVoiceCall); + assertTrue(mSarInfo.mIsWifiSapEnabled); + assertFalse(mSarInfo.mIsWifiScanOnlyEnabled); + } + + /** + * Test that for a device that has SAR enabled, SAR sensor enabled, near head, and when + * wifi sta is enabled, turning on sap then turning it off. + */ + @Test + public void testSarMgr_enabledTxPowerScenario_staOn_sapOnOff() throws Exception { + createSarManager(true, true); + + InOrder inOrder = inOrder(mWifiNative); + + /* Sensor event */ + sendSensorEvent(SAR_SENSOR_EVENT_HEAD); + inOrder.verify(mWifiNative, never()).selectTxPowerScenario(any(SarInfo.class)); + + /* Enable WiFi Client State */ + mSarMgr.setClientWifiState(WifiManager.WIFI_STATE_ENABLED); + captureSarInfo(mWifiNative); + + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HEAD, mSarInfo.mSensorState); + assertFalse(mSarInfo.mIsWifiSapEnabled); + + /* Enable WiFi SoftAP State */ + mSarMgr.setSapWifiState(WifiManager.WIFI_AP_STATE_ENABLED); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HEAD, mSarInfo.mSensorState); + assertTrue(mSarInfo.mIsWifiSapEnabled); + + /* Disable Wifi SoftAP state */ + mSarMgr.setSapWifiState(WifiManager.WIFI_AP_STATE_DISABLED); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HEAD, mSarInfo.mSensorState); + assertFalse(mSarInfo.mIsWifiSapEnabled); + } + + /** + * Test that for a device that has SAR enabled, SAR sensor enabled, Near body, and when + * disabling wifi softAP while Wifi Sta is also disabled, no update to the HAL for Tx + * power scenario is issued. + * Then, when wifi client is enabled, the Tx Power scenario is set. + * This is to verify that no call to update tx power when all wifi modes are disabled. + */ + @Test + public void testSarMgr_enabledTxPowerScenario_sapOnOff_staOffOn() throws Exception { + createSarManager(true, true); + + InOrder inOrder = inOrder(mWifiNative); + + /* Sensor event */ + sendSensorEvent(SAR_SENSOR_EVENT_BODY); + inOrder.verify(mWifiNative, never()).selectTxPowerScenario(any(SarInfo.class)); + + /* Enable WiFi softAP State */ + mSarMgr.setSapWifiState(WifiManager.WIFI_AP_STATE_ENABLED); + captureSarInfo(mWifiNative); + + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_BODY, mSarInfo.mSensorState); + assertTrue(mSarInfo.mIsWifiSapEnabled); + + /* Disable Wifi SoftAP state */ + mSarMgr.setSapWifiState(WifiManager.WIFI_AP_STATE_DISABLED); + inOrder.verify(mWifiNative, never()).selectTxPowerScenario(any(SarInfo.class)); + + /* Enable WiFi Clinet State */ + mSarMgr.setClientWifiState(WifiManager.WIFI_STATE_ENABLED); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_BODY, mSarInfo.mSensorState); + assertFalse(mSarInfo.mIsWifiSapEnabled); + } + + /** + * Test that for a device that has SAR enabled, when scan-only state is enabled with both SoftAP + * and Client states disabled, the SarInfo is reported with proper values. + */ + @Test + public void testSarMgr_enabledTxPowerScenario_staOff_sapOff_scanOnlyOn() throws Exception { + createSarManager(true, false); + + /* Enable Wifi ScanOnly State */ + mSarMgr.setScanOnlyWifiState(WifiManager.WIFI_STATE_ENABLED); + captureSarInfo(mWifiNative); + + verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertFalse(mSarInfo.mIsWifiSapEnabled); + assertTrue(mSarInfo.mIsWifiScanOnlyEnabled); + } + + /** + * Test that for a device that has SAR enabled, when scan-only state is enabled, and then + * client state is enabled, no additional setting of Tx power scenario is initiated + */ + @Test + public void testSarMgr_enabledTxPowerScenario_staOn_sapOff_scanOnlyOn() throws Exception { + createSarManager(true, false); + + InOrder inOrder = inOrder(mWifiNative); + + /* Enable Wifi ScanOnly State */ + mSarMgr.setScanOnlyWifiState(WifiManager.WIFI_STATE_ENABLED); + captureSarInfo(mWifiNative); + + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertFalse(mSarInfo.mIsWifiSapEnabled); + assertTrue(mSarInfo.mIsWifiScanOnlyEnabled); + + /* Now enable Client state */ + mSarMgr.setClientWifiState(WifiManager.WIFI_STATE_ENABLED); + inOrder.verify(mWifiNative, never()).selectTxPowerScenario(any(SarInfo.class)); + } + + /** + * Test the success case for for devices supporting SAR, with no SAR sensor support, + * Wifi enabled, SoftAP enabled, wifi disabled, scan-only enabled, SoftAP disabled. + * + * SarManager should report these changes as they occur(only when changes occur to + * inputs affecting the SAR scenario). + */ + @Test + public void testSarMgr_enabledTxPowerScenario_wifi_sap_scanOnly() throws Exception { + createSarManager(true, false); + + InOrder inOrder = inOrder(mWifiNative); + + /* Enable WiFi State */ + mSarMgr.setClientWifiState(WifiManager.WIFI_STATE_ENABLED); + captureSarInfo(mWifiNative); + + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertFalse(mSarInfo.mIsVoiceCall); + assertFalse(mSarInfo.mIsWifiSapEnabled); + assertFalse(mSarInfo.mIsWifiScanOnlyEnabled); + + /* Enable SoftAP state */ + mSarMgr.setSapWifiState(WifiManager.WIFI_AP_STATE_ENABLED); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertFalse(mSarInfo.mIsVoiceCall); + assertTrue(mSarInfo.mIsWifiSapEnabled); + assertFalse(mSarInfo.mIsWifiScanOnlyEnabled); + + /* Disable WiFi State */ + mSarMgr.setClientWifiState(WifiManager.WIFI_STATE_DISABLED); + inOrder.verify(mWifiNative, never()).selectTxPowerScenario(any(SarInfo.class)); + + /* Enable ScanOnly state */ + mSarMgr.setScanOnlyWifiState(WifiManager.WIFI_STATE_ENABLED); + inOrder.verify(mWifiNative, never()).selectTxPowerScenario(any(SarInfo.class)); + + /* Disable SoftAP state */ + mSarMgr.setSapWifiState(WifiManager.WIFI_AP_STATE_DISABLED); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertFalse(mSarInfo.mIsVoiceCall); + assertFalse(mSarInfo.mIsWifiSapEnabled); + assertTrue(mSarInfo.mIsWifiScanOnlyEnabled); + } + + /** + * Test the error case for devices supporting SAR, with no SAR sensor support, + * Wifi enabled, SoftAP enabled, wifi disabled, scan-only enabled, SoftAP disabled + * Throughout this test case, calls to the hal return with error. + */ + @Test + public void testSarMgr_enabledTxPowerScenario_error_wifi_sap_scanOnly() throws Exception { + createSarManager(true, false); + + when(mWifiNative.selectTxPowerScenario(any(SarInfo.class))).thenReturn(false); + InOrder inOrder = inOrder(mWifiNative); + + /* Enable WiFi State */ + mSarMgr.setClientWifiState(WifiManager.WIFI_STATE_ENABLED); + captureSarInfo(mWifiNative); + + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertFalse(mSarInfo.mIsVoiceCall); + assertFalse(mSarInfo.mIsWifiSapEnabled); + assertFalse(mSarInfo.mIsWifiScanOnlyEnabled); + + /* Enable SoftAP state */ + mSarMgr.setSapWifiState(WifiManager.WIFI_AP_STATE_ENABLED); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertFalse(mSarInfo.mIsVoiceCall); + assertTrue(mSarInfo.mIsWifiSapEnabled); + assertFalse(mSarInfo.mIsWifiScanOnlyEnabled); + + /* Disable WiFi State, reporting should still happen */ + mSarMgr.setClientWifiState(WifiManager.WIFI_STATE_DISABLED); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertFalse(mSarInfo.mIsVoiceCall); + assertTrue(mSarInfo.mIsWifiSapEnabled); + assertFalse(mSarInfo.mIsWifiScanOnlyEnabled); + assertFalse(mSarInfo.mIsWifiClientEnabled); + + /* Enable ScanOnly state */ + mSarMgr.setScanOnlyWifiState(WifiManager.WIFI_STATE_ENABLED); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertFalse(mSarInfo.mIsVoiceCall); + assertTrue(mSarInfo.mIsWifiSapEnabled); + assertTrue(mSarInfo.mIsWifiScanOnlyEnabled); + assertFalse(mSarInfo.mIsWifiClientEnabled); + + /* Disable SoftAP state */ + mSarMgr.setSapWifiState(WifiManager.WIFI_AP_STATE_DISABLED); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertFalse(mSarInfo.mIsVoiceCall); + assertFalse(mSarInfo.mIsWifiSapEnabled); + assertTrue(mSarInfo.mIsWifiScanOnlyEnabled); + assertFalse(mSarInfo.mIsWifiClientEnabled); } } diff --git a/tests/wifitests/src/com/android/server/wifi/ScanOnlyModeManagerTest.java b/tests/wifitests/src/com/android/server/wifi/ScanOnlyModeManagerTest.java index 41362eec6..8c2444592 100644 --- a/tests/wifitests/src/com/android/server/wifi/ScanOnlyModeManagerTest.java +++ b/tests/wifitests/src/com/android/server/wifi/ScanOnlyModeManagerTest.java @@ -71,6 +71,7 @@ public class ScanOnlyModeManagerTest { @Mock WifiMonitor mWifiMonitor; @Mock ScanRequestProxy mScanRequestProxy; @Mock WakeupController mWakeupController; + @Mock SarManager mSarManager; final ArgumentCaptor<WifiNative.InterfaceCallback> mInterfaceCallbackCaptor = ArgumentCaptor.forClass(WifiNative.InterfaceCallback.class); @@ -86,7 +87,7 @@ public class ScanOnlyModeManagerTest { private ScanOnlyModeManager createScanOnlyModeManager() { return new ScanOnlyModeManager(mContext, mLooper.getLooper(), mWifiNative, mListener, - mWifiMetrics, mScanRequestProxy, mWakeupController); + mWifiMetrics, mScanRequestProxy, mWakeupController, mSarManager); } private void startScanOnlyModeAndVerifyEnabled() throws Exception { @@ -112,6 +113,7 @@ public class ScanOnlyModeManagerTest { checkWifiStateChangeListenerUpdate(WIFI_STATE_ENABLED); verify(mScanRequestProxy, atLeastOnce()).clearScanResults(); verify(mScanRequestProxy, atLeastOnce()).enableScanningForHiddenNetworks(false); + verify(mSarManager).setScanOnlyWifiState(eq(WIFI_STATE_ENABLED)); } private void checkWifiScanStateChangedBroadcast(Intent intent, int expectedCurrentState) { @@ -170,6 +172,7 @@ public class ScanOnlyModeManagerTest { mLooper.dispatchAll(); verify(mWifiNative).teardownInterface(TEST_INTERFACE_NAME); verify(mContext, never()).sendStickyBroadcastAsUser(any(), eq(UserHandle.ALL)); + verify(mSarManager).setScanOnlyWifiState(eq(WIFI_STATE_DISABLED)); verifyNoMoreInteractions(mListener); } diff --git a/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java b/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java index 0b937849d..6a2c21242 100644 --- a/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java +++ b/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java @@ -90,6 +90,7 @@ public class SoftApManagerTest { @Mock FrameworkFacade mFrameworkFacade; @Mock WifiApConfigStore mWifiApConfigStore; @Mock WifiMetrics mWifiMetrics; + @Mock SarManager mSarManager; final ArgumentCaptor<WifiNative.InterfaceCallback> mWifiNativeInterfaceCallbackCaptor = ArgumentCaptor.forClass(WifiNative.InterfaceCallback.class); final ArgumentCaptor<WifiNative.SoftApListener> mSoftApListenerCaptor = @@ -133,7 +134,8 @@ public class SoftApManagerTest { mCallback, mWifiApConfigStore, config, - mWifiMetrics); + mWifiMetrics, + mSarManager); mLooper.dispatchAll(); return newSoftApManager; @@ -205,7 +207,8 @@ public class SoftApManagerTest { mCallback, mWifiApConfigStore, nullApConfig, - mWifiMetrics); + mWifiMetrics, + mSarManager); mLooper.dispatchAll(); newSoftApManager.start(); mLooper.dispatchAll(); @@ -247,7 +250,8 @@ public class SoftApManagerTest { mCallback, mWifiApConfigStore, nullApConfig, - mWifiMetrics); + mWifiMetrics, + mSarManager); mLooper.dispatchAll(); newSoftApManager.start(); mLooper.dispatchAll(); @@ -288,7 +292,8 @@ public class SoftApManagerTest { mCallback, mWifiApConfigStore, nullApConfig, - mWifiMetrics); + mWifiMetrics, + mSarManager); mLooper.dispatchAll(); newSoftApManager.start(); mLooper.dispatchAll(); @@ -329,7 +334,8 @@ public class SoftApManagerTest { mCallback, mWifiApConfigStore, softApConfig, - mWifiMetrics); + mWifiMetrics, + mSarManager); mLooper.dispatchAll(); newSoftApManager.start(); mLooper.dispatchAll(); @@ -371,7 +377,8 @@ public class SoftApManagerTest { mCallback, mWifiApConfigStore, softApConfig, - mWifiMetrics); + mWifiMetrics, + mSarManager); mLooper.dispatchAll(); newSoftApManager.start(); mLooper.dispatchAll(); @@ -408,7 +415,8 @@ public class SoftApManagerTest { mCallback, mWifiApConfigStore, softApModeConfig, - mWifiMetrics); + mWifiMetrics, + mSarManager); mLooper.dispatchAll(); newSoftApManager.start(); @@ -429,6 +437,7 @@ public class SoftApManagerTest { mLooper.dispatchAll(); /* Verify no state changes. */ verify(mCallback, never()).onStateChanged(anyInt(), anyInt()); + verify(mSarManager, never()).setSapWifiState(anyInt()); verify(mContext, never()).sendStickyBroadcastAsUser(any(), any()); verify(mWifiNative, never()).teardownInterface(anyString()); } @@ -459,6 +468,7 @@ public class SoftApManagerTest { softApModeConfig.getTargetMode()); order.verify(mCallback).onStateChanged(WifiManager.WIFI_AP_STATE_DISABLED, 0); + verify(mSarManager).setSapWifiState(WifiManager.WIFI_AP_STATE_DISABLED); order.verify(mContext).sendStickyBroadcastAsUser(intentCaptor.capture(), eq(UserHandle.ALL)); checkApStateChangedBroadcast(intentCaptor.getValue(), WIFI_AP_STATE_DISABLED, @@ -938,6 +948,7 @@ public class SoftApManagerTest { mLooper.dispatchAll(); order.verify(mCallback).onStateChanged(WifiManager.WIFI_AP_STATE_ENABLED, 0); order.verify(mCallback).onNumClientsChanged(0); + verify(mSarManager).setSapWifiState(WifiManager.WIFI_AP_STATE_ENABLED); verify(mContext, times(2)).sendStickyBroadcastAsUser(intentCaptor.capture(), eq(UserHandle.ALL)); List<Intent> capturedIntents = intentCaptor.getAllValues(); diff --git a/tests/wifitests/src/com/android/server/wifi/WifiNativeTest.java b/tests/wifitests/src/com/android/server/wifi/WifiNativeTest.java index 31e7e553c..fbdc8f507 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiNativeTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiNativeTest.java @@ -586,4 +586,26 @@ public class WifiNativeTest { verify(mWificondControl).startHostapd(WIFI_IFACE_NAME, mockListener); verify(mWifiMetrics).incrementNumSetupSoftApInterfaceFailureDueToHostapd(); } + + /** + * Test that selectTxPowerScenario() calls into WifiVendorHal (success case) + */ + @Test + public void testSelectTxPowerScenario_success() throws Exception { + when(mWifiVendorHal.selectTxPowerScenario(any(SarInfo.class))).thenReturn(true); + SarInfo sarInfo = new SarInfo(true); + assertTrue(mWifiNative.selectTxPowerScenario(sarInfo)); + verify(mWifiVendorHal).selectTxPowerScenario(sarInfo); + } + + /** + * Test that selectTxPowerScenario() calls into WifiVendorHal (failure case) + */ + @Test + public void testSelectTxPowerScenario_failure() throws Exception { + when(mWifiVendorHal.selectTxPowerScenario(any(SarInfo.class))).thenReturn(false); + SarInfo sarInfo = new SarInfo(true); + assertFalse(mWifiNative.selectTxPowerScenario(sarInfo)); + verify(mWifiVendorHal).selectTxPowerScenario(sarInfo); + } } diff --git a/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java b/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java index 0f8f56f66..fc030b83c 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java @@ -32,6 +32,7 @@ import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; @@ -99,6 +100,7 @@ import com.android.server.wifi.util.NativeUtil; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; +import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.stubbing.Answer; @@ -119,6 +121,7 @@ public class WifiVendorHalTest { private static final String TEST_IFACE_NAME = "wlan0"; private static final String TEST_IFACE_NAME_1 = "wlan1"; private static final MacAddress TEST_MAC_ADDRESS = MacAddress.fromString("ee:33:a2:94:10:92"); + private static final int SAR_SENSOR_INVALID_STATE = -6; WifiVendorHal mWifiVendorHal; private WifiStatus mWifiStatusSuccess; @@ -1997,67 +2000,433 @@ public class WifiVendorHalTest { } /** - * Test the new selectTxPowerScenario HIDL method invocation. This should return failure if the - * HAL service is exposing the 1.0 interface. + * Test the selectTxPowerScenario HIDL method invocation for 1.0 interface. + * This should return failure since SAR is not supported for this interface version. */ @Test - public void testSelectTxPowerScenario() throws RemoteException { + public void testSelectTxPowerScenario_1_0() throws RemoteException { + // Create a SAR info record (no sensor support) + SarInfo sarInfo = new SarInfo(false); + sarInfo.mIsVoiceCall = true; + assertTrue(mWifiVendorHal.startVendorHalSta()); // Should fail because we exposed the 1.0 IWifiChip. - assertFalse( - mWifiVendorHal.selectTxPowerScenario(WifiNative.TX_POWER_SCENARIO_VOICE_CALL)); + assertFalse(mWifiVendorHal.selectTxPowerScenario(sarInfo)); verify(mIWifiChipV11, never()).selectTxPowerScenario(anyInt()); mWifiVendorHal.stopVendorHal(); + } + + /** + * Test the selectTxPowerScenario HIDL method invocation for 1.1 interface. + * This should return success. + */ + @Test + public void testSelectTxPowerScenario_1_1() throws RemoteException { + // Create a SAR info record (no sensor support) + SarInfo sarInfo = new SarInfo(false); + sarInfo.mIsVoiceCall = true; // Now expose the 1.1 IWifiChip. mWifiVendorHal = new WifiVendorHalSpyV1_1(mHalDeviceManager, mLooper.getLooper()); when(mIWifiChipV11.selectTxPowerScenario(anyInt())).thenReturn(mWifiStatusSuccess); assertTrue(mWifiVendorHal.startVendorHalSta()); - assertTrue( - mWifiVendorHal.selectTxPowerScenario(WifiNative.TX_POWER_SCENARIO_VOICE_CALL)); + assertTrue(mWifiVendorHal.selectTxPowerScenario(sarInfo)); verify(mIWifiChipV11).selectTxPowerScenario( eq(android.hardware.wifi.V1_1.IWifiChip.TxPowerScenario.VOICE_CALL)); verify(mIWifiChipV11, never()).resetTxPowerScenario(); mWifiVendorHal.stopVendorHal(); } + /** + * Test the selectTxPowerScenario HIDL method invocation for 1.2 interface. + * This should return success. + */ + @Test + public void testSelectTxPowerScenario_1_2() throws RemoteException { + // Create a SAR info record (no sensor support) + SarInfo sarInfo = new SarInfo(false); + sarInfo.mIsVoiceCall = true; + + // Now expose the 1.2 IWifiChip + mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mLooper.getLooper()); + when(mIWifiChipV12.selectTxPowerScenario_1_2(anyInt())).thenReturn(mWifiStatusSuccess); + + assertTrue(mWifiVendorHal.startVendorHalSta()); + assertTrue(mWifiVendorHal.selectTxPowerScenario(sarInfo)); + verify(mIWifiChipV12).selectTxPowerScenario_1_2( + eq(android.hardware.wifi.V1_2.IWifiChip.TxPowerScenario.VOICE_CALL)); + verify(mIWifiChipV12, never()).resetTxPowerScenario(); + mWifiVendorHal.stopVendorHal(); + } + /** - * Test the new resetTxPowerScenario HIDL method invocation. This should return failure if the - * HAL service is exposing the 1.0 interface. + * Test the resetTxPowerScenario HIDL method invocation for 1.0 interface. + * This should return failure since it does not supprt SAR. */ @Test - public void testResetTxPowerScenario() throws RemoteException { + public void testResetTxPowerScenario_1_0() throws RemoteException { + // Create a SAR info record (no sensor support) + SarInfo sarInfo = new SarInfo(false); + assertTrue(mWifiVendorHal.startVendorHalSta()); // Should fail because we exposed the 1.0 IWifiChip. - assertFalse(mWifiVendorHal.selectTxPowerScenario(WifiNative.TX_POWER_SCENARIO_NORMAL)); + assertFalse(mWifiVendorHal.selectTxPowerScenario(sarInfo)); verify(mIWifiChipV11, never()).resetTxPowerScenario(); mWifiVendorHal.stopVendorHal(); + } + + /** + * Test the resetTxPowerScenario HIDL method invocation for 1.1 interface. + * This should return success. + */ + @Test + public void testResetTxPowerScenario_1_1() throws RemoteException { + // Create a SAR info record (no sensor support) + SarInfo sarInfo = new SarInfo(false); // Now expose the 1.1 IWifiChip. mWifiVendorHal = new WifiVendorHalSpyV1_1(mHalDeviceManager, mLooper.getLooper()); when(mIWifiChipV11.resetTxPowerScenario()).thenReturn(mWifiStatusSuccess); assertTrue(mWifiVendorHal.startVendorHalSta()); - assertTrue(mWifiVendorHal.selectTxPowerScenario(WifiNative.TX_POWER_SCENARIO_NORMAL)); + assertTrue(mWifiVendorHal.selectTxPowerScenario(sarInfo)); verify(mIWifiChipV11).resetTxPowerScenario(); verify(mIWifiChipV11, never()).selectTxPowerScenario(anyInt()); mWifiVendorHal.stopVendorHal(); } /** - * Test the new selectTxPowerScenario HIDL method invocation with a bad scenario index. + * Test resetting SAR scenario when not needed, should return true without invoking + * the HAL method. + * This is using HAL 1.1 interface. */ @Test - public void testInvalidSelectTxPowerScenario() throws RemoteException { + public void testResetTxPowerScenario_not_needed_1_1() throws RemoteException { + InOrder inOrder = inOrder(mIWifiChipV11); + + // Create a SAR info record (no sensor support) + SarInfo sarInfo = new SarInfo(false); + + // Now expose the 1.1 IWifiChip. + mWifiVendorHal = new WifiVendorHalSpyV1_1(mHalDeviceManager, mLooper.getLooper()); + when(mIWifiChipV11.resetTxPowerScenario()).thenReturn(mWifiStatusSuccess); + + assertTrue(mWifiVendorHal.startVendorHalSta()); + + /* Calling reset once */ + assertTrue(mWifiVendorHal.selectTxPowerScenario(sarInfo)); + inOrder.verify(mIWifiChipV11).resetTxPowerScenario(); + inOrder.verify(mIWifiChipV11, never()).selectTxPowerScenario(anyInt()); + sarInfo.reportingSuccessful(); + + /* Calling reset second time */ + assertTrue(mWifiVendorHal.selectTxPowerScenario(sarInfo)); + inOrder.verify(mIWifiChipV11, never()).resetTxPowerScenario(); + inOrder.verify(mIWifiChipV11, never()).selectTxPowerScenario(anyInt()); + + mWifiVendorHal.stopVendorHal(); + } + + /** + * Test the new resetTxPowerScenario HIDL method invocation for 1.2 interface. + * This should return success. + */ + @Test + public void testResetTxPowerScenario_1_2() throws RemoteException { + // Create a SAR info record (no sensor support) + SarInfo sarInfo = new SarInfo(false); + + // Now expose the 1.2 IWifiChip. + mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mLooper.getLooper()); + when(mIWifiChipV12.resetTxPowerScenario()).thenReturn(mWifiStatusSuccess); + + assertTrue(mWifiVendorHal.startVendorHalSta()); + assertTrue(mWifiVendorHal.selectTxPowerScenario(sarInfo)); + verify(mIWifiChipV12).resetTxPowerScenario(); + verify(mIWifiChipV12, never()).selectTxPowerScenario_1_2(anyInt()); + mWifiVendorHal.stopVendorHal(); + } + + /** + * Test resetting SAR scenario when not needed, should return true without invoking + * the HAL method. + * This is using HAL 1.2 interface. + */ + @Test + public void testResetTxPowerScenario_not_needed_1_2() throws RemoteException { + InOrder inOrder = inOrder(mIWifiChipV12); + + // Create a SAR info record (no sensor support) + SarInfo sarInfo = new SarInfo(false); + + // Now expose the 1.2 IWifiChip. + mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mLooper.getLooper()); + when(mIWifiChipV12.resetTxPowerScenario()).thenReturn(mWifiStatusSuccess); + + assertTrue(mWifiVendorHal.startVendorHalSta()); + + /* Calling reset once */ + assertTrue(mWifiVendorHal.selectTxPowerScenario(sarInfo)); + inOrder.verify(mIWifiChipV12).resetTxPowerScenario(); + inOrder.verify(mIWifiChipV12, never()).selectTxPowerScenario(anyInt()); + sarInfo.reportingSuccessful(); + + /* Calling reset second time */ + assertTrue(mWifiVendorHal.selectTxPowerScenario(sarInfo)); + inOrder.verify(mIWifiChipV12, never()).resetTxPowerScenario(); + inOrder.verify(mIWifiChipV12, never()).selectTxPowerScenario(anyInt()); + + mWifiVendorHal.stopVendorHal(); + } + + /** + * Test the selectTxPowerScenario HIDL method invocation with sensor related scenarios + * to IWifiChip 1.2 interface + */ + @Test + public void testHeadSensorScenarios_SelectTxPowerV1_2() throws RemoteException { + // Create a SAR info record (with sensor support) + SarInfo sarInfo = new SarInfo(true); + sarInfo.mSensorState = SarInfo.SAR_SENSOR_NEAR_HEAD; + + // Expose the 1.2 IWifiChip. + mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mLooper.getLooper()); + when(mIWifiChipV12.selectTxPowerScenario_1_2(anyInt())).thenReturn(mWifiStatusSuccess); + + // ON_HEAD_CELL_OFF + assertTrue(mWifiVendorHal.startVendorHalSta()); + assertTrue(mWifiVendorHal.selectTxPowerScenario(sarInfo)); + verify(mIWifiChipV12).selectTxPowerScenario_1_2( + eq(android.hardware.wifi.V1_2.IWifiChip.TxPowerScenario.ON_HEAD_CELL_OFF)); + verify(mIWifiChipV12, never()).resetTxPowerScenario(); + mWifiVendorHal.stopVendorHal(); + } + + /** + * Test setting SAR scenario when not needed, should return true without invoking + * the HAL method. + * This is using HAL 1.2 interface. + */ + @Test + public void testSetTxPowerScenario_not_needed_1_2() throws RemoteException { + InOrder inOrder = inOrder(mIWifiChipV12); + + // Create a SAR info record (no sensor support) + SarInfo sarInfo = new SarInfo(true); + sarInfo.mSensorState = SarInfo.SAR_SENSOR_NEAR_HEAD; + + // Now expose the 1.2 IWifiChip. + mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mLooper.getLooper()); + when(mIWifiChipV12.selectTxPowerScenario_1_2(anyInt())).thenReturn(mWifiStatusSuccess); + + assertTrue(mWifiVendorHal.startVendorHalSta()); + + /* Calling set once */ + assertTrue(mWifiVendorHal.selectTxPowerScenario(sarInfo)); + inOrder.verify(mIWifiChipV12).selectTxPowerScenario_1_2( + eq(android.hardware.wifi.V1_2.IWifiChip.TxPowerScenario.ON_HEAD_CELL_OFF)); + inOrder.verify(mIWifiChipV12, never()).resetTxPowerScenario(); + sarInfo.reportingSuccessful(); + + /* Calling set second time */ + assertTrue(mWifiVendorHal.selectTxPowerScenario(sarInfo)); + inOrder.verify(mIWifiChipV12, never()).resetTxPowerScenario(); + inOrder.verify(mIWifiChipV12, never()).selectTxPowerScenario(anyInt()); + + mWifiVendorHal.stopVendorHal(); + } + + /** + * Test the selectTxPowerScenairo HIDL method invocation with sensor events for + * IWifiChip 1.2 interface (Near hand event) along with a voice call. + * This should be reverted to BODY events (First with CELL_OFF followed by CELL_ON). + */ + @Test + public void testHandSensorScenarios_SelectTxPowerV1_2() throws RemoteException { + // Create a SAR info record (with sensor support) + SarInfo sarInfo = new SarInfo(true); + sarInfo.mSensorState = SarInfo.SAR_SENSOR_NEAR_HAND; + + // Expose the 1.2 IWifiChip. + mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mLooper.getLooper()); + when(mIWifiChipV12.selectTxPowerScenario_1_2(anyInt())).thenReturn(mWifiStatusSuccess); + + assertTrue(mWifiVendorHal.startVendorHalSta()); + + // First select a scenario with cell off + assertTrue(mWifiVendorHal.selectTxPowerScenario(sarInfo)); + verify(mIWifiChipV12).selectTxPowerScenario_1_2( + eq(android.hardware.wifi.V1_2.IWifiChip.TxPowerScenario.ON_BODY_CELL_OFF)); + + // Then select a scenario with cell on + sarInfo.mIsVoiceCall = true; + assertTrue(mWifiVendorHal.selectTxPowerScenario(sarInfo)); + verify(mIWifiChipV12).selectTxPowerScenario_1_2( + eq(android.hardware.wifi.V1_2.IWifiChip.TxPowerScenario.ON_BODY_CELL_ON)); + + mWifiVendorHal.stopVendorHal(); + } + + /** + * Test the selectTxPowerScenario HIDL method invocation with a sensor info to IWifiChip + * 1.1 interface. + * Sensor mode should be ignored, and act only based on Cell on/off. + */ + @Test + public void testOnHeadCellOffOn_SelectTxPowerScenarioV1_1() throws RemoteException { + // Create a SAR info record (with sensor support) + SarInfo sarInfo = new SarInfo(true); + sarInfo.mSensorState = SarInfo.SAR_SENSOR_NEAR_HEAD; + // Expose the 1.1 IWifiChip. mWifiVendorHal = new WifiVendorHalSpyV1_1(mHalDeviceManager, mLooper.getLooper()); when(mIWifiChipV11.selectTxPowerScenario(anyInt())).thenReturn(mWifiStatusSuccess); + when(mIWifiChipV11.resetTxPowerScenario()).thenReturn(mWifiStatusSuccess); assertTrue(mWifiVendorHal.startVendorHalSta()); - assertFalse(mWifiVendorHal.selectTxPowerScenario(-6)); - verify(mIWifiChipV11, never()).selectTxPowerScenario(anyInt()); - verify(mIWifiChipV11, never()).resetTxPowerScenario(); + + // First select a scenario with cell off + assertTrue(mWifiVendorHal.selectTxPowerScenario(sarInfo)); + verify(mIWifiChipV11).resetTxPowerScenario(); + + // Then select a scenario with cell on + sarInfo.mIsVoiceCall = true; + assertTrue(mWifiVendorHal.selectTxPowerScenario(sarInfo)); + verify(mIWifiChipV11).selectTxPowerScenario( + eq(android.hardware.wifi.V1_1.IWifiChip.TxPowerScenario.VOICE_CALL)); + + mWifiVendorHal.stopVendorHal(); + } + + /** + * Test the new selectTxPowerScenario HIDL method invocation with a bad input. + * This should not result into any calls to the HAL. + * Use IWifiChip 1.2 interface + */ + @Test + public void testInvalidSelectTxPowerScenario_1_2() throws RemoteException { + // Create a SAR info record (with sensor support) + SarInfo sarInfo = new SarInfo(true); + sarInfo.mSensorState = SAR_SENSOR_INVALID_STATE; + + // Expose the 1.2 IWifiChip. + mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mLooper.getLooper()); + + assertTrue(mWifiVendorHal.startVendorHalSta()); + assertFalse(mWifiVendorHal.selectTxPowerScenario(sarInfo)); + verify(mIWifiChipV12, never()).selectTxPowerScenario(anyInt()); + verify(mIWifiChipV12, never()).resetTxPowerScenario(); + mWifiVendorHal.stopVendorHal(); + } + + /** + * Test the selectTxPowerScenario HIDL method invocation with IWifiChip 1.2 interface. + * The following inputs: + * - Sensor support is enabled + * - Sensor state is NEAR_HEAD + * - SAP is enabled + * - No voice call + */ + @Test + public void testSelectTxPowerScenario_1_2_head_sap() throws RemoteException { + // Create a SAR info record (with sensor support) + SarInfo sarInfo = new SarInfo(true); + sarInfo.mSensorState = SarInfo.SAR_SENSOR_NEAR_HEAD; + sarInfo.mIsWifiSapEnabled = true; + sarInfo.mIsVoiceCall = false; + + // Expose the 1.2 IWifiChip. + mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mLooper.getLooper()); + when(mIWifiChipV12.selectTxPowerScenario_1_2(anyInt())).thenReturn(mWifiStatusSuccess); + + assertTrue(mWifiVendorHal.startVendorHalSta()); + assertTrue(mWifiVendorHal.selectTxPowerScenario(sarInfo)); + verify(mIWifiChipV12).selectTxPowerScenario_1_2( + eq(android.hardware.wifi.V1_2.IWifiChip.TxPowerScenario.ON_HEAD_CELL_ON)); + + mWifiVendorHal.stopVendorHal(); + } + + /** + * Test the selectTxPowerScenario HIDL method invocation with IWifiChip 1.2 interface. + * The following inputs: + * - Sensor support is enabled + * - Sensor state is NEAR_HEAD + * - SAP is enabled + * - voice call is enabled + */ + @Test + public void testSelectTxPowerScenario_1_2_head_sap_call() throws RemoteException { + // Create a SAR info record (with sensor support) + SarInfo sarInfo = new SarInfo(true); + sarInfo.mSensorState = SarInfo.SAR_SENSOR_NEAR_HEAD; + sarInfo.mIsWifiSapEnabled = true; + sarInfo.mIsVoiceCall = true; + + // Expose the 1.2 IWifiChip. + mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mLooper.getLooper()); + when(mIWifiChipV12.selectTxPowerScenario_1_2(anyInt())).thenReturn(mWifiStatusSuccess); + + assertTrue(mWifiVendorHal.startVendorHalSta()); + assertTrue(mWifiVendorHal.selectTxPowerScenario(sarInfo)); + verify(mIWifiChipV12).selectTxPowerScenario_1_2( + eq(android.hardware.wifi.V1_2.IWifiChip.TxPowerScenario.ON_HEAD_CELL_ON)); + + mWifiVendorHal.stopVendorHal(); + } + + /** + * Test the selectTxPowerScenario HIDL method invocation with IWifiChip 1.2 interface. + * The following inputs: + * - Sensor support is enabled + * - Sensor state is FREE_SPACE + * - SAP is enabled + * - No voice call + */ + @Test + public void testSelectTxPowerScenario_1_2_freespace_sap() throws RemoteException { + // Create a SAR info record (with sensor support) + SarInfo sarInfo = new SarInfo(true); + sarInfo.mSensorState = SarInfo.SAR_SENSOR_FREE_SPACE; + sarInfo.mIsWifiSapEnabled = true; + sarInfo.mIsVoiceCall = false; + + // Expose the 1.2 IWifiChip. + mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mLooper.getLooper()); + when(mIWifiChipV12.resetTxPowerScenario()).thenReturn(mWifiStatusSuccess); + + assertTrue(mWifiVendorHal.startVendorHalSta()); + assertTrue(mWifiVendorHal.selectTxPowerScenario(sarInfo)); + + verify(mIWifiChipV12).resetTxPowerScenario(); + verify(mIWifiChipV12, never()).selectTxPowerScenario_1_2(anyInt()); + mWifiVendorHal.stopVendorHal(); + } + + /** + * Test the selectTxPowerScenario HIDL method invocation with IWifiChip 1.2 interface. + * The following inputs: + * - Sensor support is disabled + * - SAP is enabled + * - No voice call + */ + @Test + public void testSelectTxPowerScenario_1_2_no_sensors_sap() throws RemoteException { + // Create a SAR info record (with no sensor support) + SarInfo sarInfo = new SarInfo(false); + sarInfo.mIsWifiSapEnabled = true; + sarInfo.mIsVoiceCall = false; + + // Expose the 1.2 IWifiChip. + mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mLooper.getLooper()); + when(mIWifiChipV12.resetTxPowerScenario()).thenReturn(mWifiStatusSuccess); + + assertTrue(mWifiVendorHal.startVendorHalSta()); + assertTrue(mWifiVendorHal.selectTxPowerScenario(sarInfo)); + + verify(mIWifiChipV12).resetTxPowerScenario(); + verify(mIWifiChipV12, never()).selectTxPowerScenario_1_2(anyInt()); mWifiVendorHal.stopVendorHal(); } |