diff options
author | Bill Yi <byi@google.com> | 2018-11-30 11:54:06 -0800 |
---|---|---|
committer | android-build-merger <android-build-merger@google.com> | 2018-11-30 11:54:06 -0800 |
commit | 7c422a55da4ae4d0fda54df639b3f7d944a0edb6 (patch) | |
tree | 2ca9f9b670957c488e88a77dc8f17770fac52817 | |
parent | d028902bb2d58015fba7a98a78ba46e067fa51ee (diff) | |
parent | 8c9f448f5d1328340be1b860fc47b80b0b474011 (diff) | |
download | android_frameworks_opt_net_wifi-7c422a55da4ae4d0fda54df639b3f7d944a0edb6.tar.gz android_frameworks_opt_net_wifi-7c422a55da4ae4d0fda54df639b3f7d944a0edb6.tar.bz2 android_frameworks_opt_net_wifi-7c422a55da4ae4d0fda54df639b3f7d944a0edb6.zip |
Merge pi-qpr1-release PQ1A.181105.017.A1 to pi-platform-release
am: 8c9f448f5d
Change-Id: I30561056172a6c3150030be8dda74b5ed956e4b8
27 files changed, 3146 insertions, 278 deletions
diff --git a/service/java/com/android/server/wifi/FrameworkFacade.java b/service/java/com/android/server/wifi/FrameworkFacade.java index 15b9cc738..26cb4ff7b 100644 --- a/service/java/com/android/server/wifi/FrameworkFacade.java +++ b/service/java/com/android/server/wifi/FrameworkFacade.java @@ -136,17 +136,6 @@ public class FrameworkFacade { return true; } - /** - * Create a new instance of WifiApConfigStore. - * @param context reference to a Context - * @param backupManagerProxy reference to a BackupManagerProxy - * @return an instance of WifiApConfigStore - */ - public WifiApConfigStore makeApConfigStore(Context context, - BackupManagerProxy backupManagerProxy) { - return new WifiApConfigStore(context, backupManagerProxy); - } - public long getTxPackets(String iface) { return TrafficStats.getTxPackets(iface); } 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..a62307e13 --- /dev/null +++ b/service/java/com/android/server/wifi/SarInfo.java @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wifi; + +import java.io.FileDescriptor; +import java.io.PrintWriter; + +/** + * This class represents the list of SAR inputs that will be used to select the proper + * power profile. + * This includes: + * - SAR sensor status + * - Is there an ongoing voice call + * - Is SoftAP active + * It also contains info about state of the other Wifi modes + * - Client mode (Sta) + * - ScanOnly mode + * It also keeps history for the reporting of SAR states/scenario to avoid unnecessary reporting + * - keeps track of the last reported states + * - keeps track of the last reported SAR scenario + * - keeps track of if all wifi modes were disabled (no reporting should happen then) + */ +public class SarInfo { + /** + * This value is used as an initial value for the last reported scenario + * It is intended to be different than all valid SAR scenario values (including the + * reset value). + * Using this to initialize the lastReportedScenario results in that the first scenario + * (including reset) would be reported. + */ + public static final int INITIAL_SAR_SCENARIO = -2; + + /** + * This value is used for the reset scenario (no TX Power backoff) + * Valid scenario values only include scenarios with Tx Power backoff, + * so we need this one to represent the "No backoff" case. + */ + public static final int RESET_SAR_SCENARIO = -1; + + private static final String SAR_SENSOR_FREE_SPACE_STR = "SAR_SENSOR_FREE_SPACE"; + private static final String SAR_SENSOR_NEAR_BODY_STR = "SAR_SENSOR_NEAR_BODY"; + private static final String SAR_SENSOR_NEAR_HAND_STR = "SAR_SENSOR_NEAR_HAND"; + private static final String SAR_SENSOR_NEAR_HEAD_STR = "SAR_SENSOR_NEAR_HEAD"; + + public static final int SAR_SENSOR_FREE_SPACE = 1; + public static final int SAR_SENSOR_NEAR_HAND = 2; + public static final int SAR_SENSOR_NEAR_HEAD = 3; + public static final int SAR_SENSOR_NEAR_BODY = 4; + + /* For Logging */ + private static final String TAG = "WifiSarInfo"; + + /* SAR support configs */ + public boolean sarVoiceCallSupported; + public boolean sarSapSupported; + public boolean sarSensorSupported; + + public int sensorState = SAR_SENSOR_FREE_SPACE; + public boolean isWifiClientEnabled = false; + public boolean isWifiSapEnabled = false; + public boolean isWifiScanOnlyEnabled = false; + public boolean isVoiceCall = false; + public int attemptedSarScenario = RESET_SAR_SCENARIO; + + private boolean mAllWifiDisabled = true; + + /* Variables representing the last successfully reported values to hal */ + private int mLastReportedSensorState = SAR_SENSOR_FREE_SPACE; + private boolean mLastReportedIsWifiSapEnabled = false; + private boolean mLastReportedIsVoiceCall = false; + private int mLastReportedScenario = INITIAL_SAR_SCENARIO; + private long mLastReportedScenarioTs = 0; + + /** + * shouldReport() + * This method returns false in the following cases: + * 1. If all Wifi modes are disabled. + * 2. Values contributing to the SAR scenario selection have not changed + * since last successful reporting. + * + * Special cases to allow for devices that require setting the SAR scenario value + * when the chip comes up (initial startup, or during operation) + * 1. This method would report true even with unchanged values from last reporting, + * if any wifi mode is just enabled after all wifi modes were disabled. + * 2. This method would report true the first time it is called with any wifi mode enabled. + */ + public boolean shouldReport() { + /* Check if all Wifi modes are disabled */ + if (!isWifiClientEnabled && !isWifiSapEnabled && !isWifiScanOnlyEnabled) { + mAllWifiDisabled = true; + return false; + } + + /* Check if Wifi was all disabled before this call */ + if (mAllWifiDisabled) { + return true; + } + + /* Check if some change happened since last successful reporting */ + if ((sensorState != mLastReportedSensorState) + || (isWifiSapEnabled != mLastReportedIsWifiSapEnabled) + || (isVoiceCall != mLastReportedIsVoiceCall)) { + return true; + } else { + return false; + } + } + + /** + * reportingSuccessful() + * This method is called when reporting SAR scenario is fully successful + * This results in caching the last reported inputs for future comparison. + */ + public void reportingSuccessful() { + mLastReportedSensorState = sensorState; + mLastReportedIsWifiSapEnabled = isWifiSapEnabled; + mLastReportedIsVoiceCall = isVoiceCall; + mLastReportedScenario = attemptedSarScenario; + mLastReportedScenarioTs = System.currentTimeMillis(); + + mAllWifiDisabled = false; + } + + /** + * resetSarScenarioNeeded() + * Returns true if a call towards HAL to reset SAR scenario would be necessary. + * Returns false if the last call to HAL was already a reset, and hence + * another call to reset the SAR scenario would be redundant. + */ + public boolean resetSarScenarioNeeded() { + return setSarScenarioNeeded(RESET_SAR_SCENARIO); + } + + /** + * setSarScenarioNeeded() + * Returns true if a call towards HAL to set SAR scenario to that value would be + * necessary. + * Returns false if the last call to HAL was to set the scenario to that value, hence, + * another call to set the SAR scenario to the same value would be redundant. + */ + public boolean setSarScenarioNeeded(int scenario) { + attemptedSarScenario = scenario; + return (mLastReportedScenario != scenario); + } + + /** + * dump() + * Dumps the state of SarInfo + */ + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println("Dump of SarInfo"); + pw.println("Current values:"); + pw.println(" Sensor state is: " + sensorStateToString(sensorState)); + pw.println(" Voice Call state is: " + isVoiceCall); + pw.println(" Wifi Client state is: " + isWifiClientEnabled); + pw.println(" Wifi Soft AP state is: " + isWifiSapEnabled); + pw.println(" Wifi ScanOnly state is: " + isWifiScanOnlyEnabled); + pw.println("Last reported values:"); + pw.println(" Sensor state is: " + sensorStateToString(mLastReportedSensorState)); + pw.println(" Soft AP state is: " + mLastReportedIsWifiSapEnabled); + pw.println(" Voice Call state is: " + mLastReportedIsVoiceCall); + pw.println("Last reported scenario: " + mLastReportedScenario); + pw.println("Reported " + (System.currentTimeMillis() - mLastReportedScenarioTs) / 1000 + + " seconds ago"); + } + + /** + * Convert SAR sensor state to string + */ + public static String sensorStateToString(int sensorState) { + switch(sensorState) { + case SAR_SENSOR_FREE_SPACE: + return SAR_SENSOR_FREE_SPACE_STR; + case SAR_SENSOR_NEAR_BODY: + return SAR_SENSOR_NEAR_BODY_STR; + case SAR_SENSOR_NEAR_HAND: + return SAR_SENSOR_NEAR_HAND_STR; + case SAR_SENSOR_NEAR_HEAD: + return SAR_SENSOR_NEAR_HEAD_STR; + default: + return "Invalid SAR sensor state"; + } + } +} diff --git a/service/java/com/android/server/wifi/SarManager.java b/service/java/com/android/server/wifi/SarManager.java index da48a8537..598e5c964 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,30 @@ 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; - /* Configuration for SAR */ - private boolean mEnableSarTxPowerLimit; + private SarInfo mSarInfo; - /* Current SAR Scenario */ - private int mCurrentSarScenario = WifiNative.TX_POWER_SCENARIO_NORMAL; + /* Configuration for SAR support */ + private boolean mSupportSarTxPowerLimit; + private boolean mSupportSarVoiceCall; + private boolean mSupportSarSoftAp; + private boolean mSupportSarSensor; + /* Sensor event definitions */ + private int mSarSensorEventFreeSpace; + private int mSarSensorEventNearBody; + private int mSarSensorEventNearHand; + private int mSarSensorEventNearHead; - /* 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 +73,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 +83,174 @@ 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 (mSupportSarTxPowerLimit) { + mSarInfo = new SarInfo(); + setSarConfigsInInfo(); + registerListeners(); + } + } - registerListeners(); + private void readSarConfigs() { + mSupportSarTxPowerLimit = mContext.getResources().getBoolean( + R.bool.config_wifi_framework_enable_sar_tx_power_limit); + /* In case SAR is disabled, + then all SAR inputs are automatically disabled as well (irrespective of the config) */ + if (!mSupportSarTxPowerLimit) { + mSupportSarVoiceCall = false; + mSupportSarSoftAp = false; + mSupportSarSensor = false; + return; + } + + /* Voice calls are supported when SAR is supported */ + mSupportSarVoiceCall = true; + + mSupportSarSoftAp = mContext.getResources().getBoolean( + R.bool.config_wifi_framework_enable_soft_ap_sar_tx_power_limit); + + mSupportSarSensor = mContext.getResources().getBoolean( + R.bool.config_wifi_framework_enable_body_proximity_sar_tx_power_limit); + + /* Read the sar sensor event Ids */ + if (mSupportSarSensor) { + 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); + } + } + + private void setSarConfigsInInfo() { + mSarInfo.sarVoiceCallSupported = mSupportSarVoiceCall; + mSarInfo.sarSapSupported = mSupportSarSoftAp; + mSarInfo.sarSensorSupported = mSupportSarSensor; + } + + private void registerListeners() { + if (mSupportSarVoiceCall) { + /* Listen for Phone State changes */ + registerPhoneStateListener(); + } + + /* Only listen for SAR sensor if supported */ + if (mSupportSarSensor) { + /* 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.sensorState = 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); + } + + /** + * Register the body/hand/head proximity sensor. + */ + private boolean registerSensorListener() { + Log.i(TAG, "Registering for Sensor notification Listener"); + return mSensorListener.register(); + } - /* Only Start listening for events if SAR is enabled */ - if (mEnableSarTxPowerLimit) { - Log.d(TAG, "Registering Listeners for the SAR Manager"); + /** + * Update Wifi Client State + */ + public void setClientWifiState(int state) { + boolean newIsEnabled; + /* No action is taken if SAR is not supported */ + if (!mSupportSarTxPowerLimit) { + return; + } - /* Listen for Phone State changes */ - registerPhoneListener(); + 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.isWifiClientEnabled != newIsEnabled) { + mSarInfo.isWifiClientEnabled = newIsEnabled; + updateSarScenario(); + } + } + + /** + * Update Wifi SoftAP State + */ + public void setSapWifiState(int state) { + boolean newIsEnabled; + /* No action is taken if SAR is not supported */ + if (!mSupportSarTxPowerLimit) { + 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.isWifiSapEnabled != newIsEnabled) { + mSarInfo.isWifiSapEnabled = newIsEnabled; + updateSarScenario(); + } + } + + /** + * Update Wifi ScanOnly State + */ + public void setScanOnlyWifiState(int state) { + boolean newIsEnabled; + /* No action is taken if SAR is not supported */ + if (!mSupportSarTxPowerLimit) { + 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.isWifiScanOnlyEnabled != newIsEnabled) { + mSarInfo.isWifiScanOnlyEnabled = newIsEnabled; + updateSarScenario(); } } @@ -104,42 +258,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.isVoiceCall != newIsVoiceCall) { + mSarInfo.isVoiceCall = 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.sensorState != newSensorState) { + Log.d(TAG, "Setting Sensor state to " + SarInfo.sensorStateToString(newSensorState)); + mSarInfo.sensorState = newSensorState; + updateSarScenario(); } } @@ -147,7 +311,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 +318,20 @@ 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("Dump of SarManager"); + pw.println("isSarSupported: " + mSupportSarTxPowerLimit); + pw.println("isSarVoiceCallSupported: " + mSupportSarVoiceCall); + pw.println("isSarSoftApSupported: " + mSupportSarSoftAp); + pw.println("isSarSensorSupported: " + mSupportSarSensor); + pw.println(""); + if (mSarInfo != null) { + mSarInfo.dump(fd, pw, args); + } } /** @@ -177,69 +342,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 (!mSupportSarTxPowerLimit || !mSupportSarVoiceCall) { + 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; + + /** + * 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; + } - if (newSarScenario != mCurrentSarScenario) { + /* 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..c880ab639 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); } @@ -228,6 +232,26 @@ public class SoftApManager implements ActiveModeManager { Log.e(TAG, "Unable to start soft AP without valid configuration"); return ERROR_GENERIC; } + // Setup country code + if (TextUtils.isEmpty(mCountryCode)) { + if (config.apBand == WifiConfiguration.AP_BAND_5GHZ) { + // Country code is mandatory for 5GHz band. + Log.e(TAG, "Invalid country code, required for setting up " + + "soft ap in 5GHz"); + return ERROR_GENERIC; + } + // Absence of country code is not fatal for 2Ghz & Any band options. + } else if (!mWifiNative.setCountryCodeHal( + mApInterfaceName, mCountryCode.toUpperCase(Locale.ROOT))) { + if (config.apBand == WifiConfiguration.AP_BAND_5GHZ) { + // Return an error if failed to set country code when AP is configured for + // 5GHz band. + Log.e(TAG, "Failed to set country code, required for setting up " + + "soft ap in 5GHz"); + return ERROR_GENERIC; + } + // Failure to set country code is not fatal for 2Ghz & Any band options. + } // Make a copy of configuration for updating AP band and channel. WifiConfiguration localConfig = new WifiConfiguration(config); @@ -241,18 +265,6 @@ public class SoftApManager implements ActiveModeManager { return result; } - // Setup country code if it is provided. - if (mCountryCode != null) { - // Country code is mandatory for 5GHz band, return an error if failed to set - // country code when AP is configured for 5GHz band. - if (!mWifiNative.setCountryCodeHal( - mApInterfaceName, mCountryCode.toUpperCase(Locale.ROOT)) - && config.apBand == WifiConfiguration.AP_BAND_5GHZ) { - Log.e(TAG, "Failed to set country code, required for setting up " - + "soft ap in 5GHz"); - return ERROR_GENERIC; - } - } if (localConfig.hiddenSSID) { Log.d(TAG, "SoftAP is a hidden network"); } @@ -491,6 +503,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 +527,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/WifiApConfigStore.java b/service/java/com/android/server/wifi/WifiApConfigStore.java index 109c0a7af..d21452c8c 100644 --- a/service/java/com/android/server/wifi/WifiApConfigStore.java +++ b/service/java/com/android/server/wifi/WifiApConfigStore.java @@ -17,15 +17,25 @@ package com.android.server.wifi; import android.annotation.NonNull; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiConfiguration.KeyMgmt; import android.os.Environment; +import android.os.Handler; +import android.os.Looper; import android.text.TextUtils; import android.util.Log; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; +import com.android.internal.notification.SystemNotificationChannels; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; @@ -44,6 +54,10 @@ import java.util.UUID; */ public class WifiApConfigStore { + // Intent when user has interacted with the softap settings change notification + public static final String ACTION_HOTSPOT_CONFIG_USER_TAPPED_CONTENT = + "com.android.server.wifi.WifiApConfigStoreUtil.HOTSPOT_CONFIG_USER_TAPPED_CONTENT"; + private static final String TAG = "WifiApConfigStore"; private static final String DEFAULT_AP_CONFIG_FILE = @@ -71,19 +85,26 @@ public class WifiApConfigStore { private ArrayList<Integer> mAllowed2GChannel = null; private final Context mContext; + private final Handler mHandler; private final String mApConfigFile; private final BackupManagerProxy mBackupManagerProxy; + private final FrameworkFacade mFrameworkFacade; private boolean mRequiresApBandConversion = false; - WifiApConfigStore(Context context, BackupManagerProxy backupManagerProxy) { - this(context, backupManagerProxy, DEFAULT_AP_CONFIG_FILE); + WifiApConfigStore(Context context, Looper looper, + BackupManagerProxy backupManagerProxy, FrameworkFacade frameworkFacade) { + this(context, looper, backupManagerProxy, frameworkFacade, DEFAULT_AP_CONFIG_FILE); } WifiApConfigStore(Context context, + Looper looper, BackupManagerProxy backupManagerProxy, + FrameworkFacade frameworkFacade, String apConfigFile) { mContext = context; + mHandler = new Handler(looper); mBackupManagerProxy = backupManagerProxy; + mFrameworkFacade = frameworkFacade; mApConfigFile = apConfigFile; String ap2GChannelListStr = mContext.getResources().getString( @@ -111,8 +132,30 @@ public class WifiApConfigStore { /* Save the default configuration to persistent storage. */ writeApConfiguration(mApConfigFile, mWifiApConfig); } + + IntentFilter filter = new IntentFilter(); + filter.addAction(ACTION_HOTSPOT_CONFIG_USER_TAPPED_CONTENT); + mContext.registerReceiver( + mBroadcastReceiver, filter, null /* broadcastPermission */, mHandler); } + private final BroadcastReceiver mBroadcastReceiver = + new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + // For now we only have one registered listener, but we easily could expand this + // to support multiple signals. Starting off with a switch to support trivial + // expansion. + switch(intent.getAction()) { + case ACTION_HOTSPOT_CONFIG_USER_TAPPED_CONTENT: + handleUserHotspotConfigTappedContent(); + break; + default: + Log.e(TAG, "Unknown action " + intent.getAction()); + } + } + }; + /** * Return the current soft access point configuration. */ @@ -145,6 +188,43 @@ public class WifiApConfigStore { return mAllowed2GChannel; } + /** + * Helper method to create and send notification to user of apBand conversion. + * + * @param packageName name of the calling app + */ + public void notifyUserOfApBandConversion(String packageName) { + Log.w(TAG, "ready to post notification - triggered by " + packageName); + Notification notification = createConversionNotification(); + NotificationManager notificationManager = (NotificationManager) + mContext.getSystemService(Context.NOTIFICATION_SERVICE); + notificationManager.notify(SystemMessage.NOTE_SOFTAP_CONFIG_CHANGED, notification); + } + + private Notification createConversionNotification() { + CharSequence title = mContext.getText(R.string.wifi_softap_config_change); + CharSequence contentSummary = mContext.getText(R.string.wifi_softap_config_change_summary); + CharSequence content = mContext.getText(R.string.wifi_softap_config_change_detailed); + int color = mContext.getResources() + .getColor(R.color.system_notification_accent_color, mContext.getTheme()); + + return new Notification.Builder(mContext, SystemNotificationChannels.NETWORK_STATUS) + .setSmallIcon(R.drawable.ic_wifi_settings) + .setPriority(Notification.PRIORITY_HIGH) + .setCategory(Notification.CATEGORY_SYSTEM) + .setContentTitle(title) + .setContentText(contentSummary) + .setContentIntent(getPrivateBroadcast(ACTION_HOTSPOT_CONFIG_USER_TAPPED_CONTENT)) + .setTicker(title) + .setShowWhen(false) + .setLocalOnly(true) + .setColor(color) + .setStyle(new Notification.BigTextStyle().bigText(content) + .setBigContentTitle(title) + .setSummaryText(contentSummary)) + .build(); + } + private WifiConfiguration apBandCheckConvert(WifiConfiguration config) { if (mRequiresApBandConversion) { // some devices are unable to support 5GHz only operation, check for 5GHz and @@ -386,4 +466,29 @@ public class WifiApConfigStore { return true; } + + /** + * Helper method to start up settings on the softap config page. + */ + private void startSoftApSettings() { + mContext.startActivity( + new Intent("com.android.settings.WIFI_TETHER_SETTINGS") + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); + } + + /** + * Helper method to trigger settings to open the softap config page + */ + private void handleUserHotspotConfigTappedContent() { + startSoftApSettings(); + NotificationManager notificationManager = (NotificationManager) + mContext.getSystemService(Context.NOTIFICATION_SERVICE); + notificationManager.cancel(SystemMessage.NOTE_SOFTAP_CONFIG_CHANGED); + } + + private PendingIntent getPrivateBroadcast(String action) { + Intent intent = new Intent(action).setPackage("android"); + return mFrameworkFacade.getBroadcast( + mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); + } } diff --git a/service/java/com/android/server/wifi/WifiController.java b/service/java/com/android/server/wifi/WifiController.java index 05ce47300..d9afc9f74 100644 --- a/service/java/com/android/server/wifi/WifiController.java +++ b/service/java/com/android/server/wifi/WifiController.java @@ -308,11 +308,7 @@ public class WifiController extends StateMachine { break; case CMD_EMERGENCY_CALL_STATE_CHANGED: case CMD_EMERGENCY_MODE_CHANGED: - boolean configWiFiDisableInECBM = - mFacade.getConfigWiFiDisableInECBM(mContext); - log("WifiController msg " + msg + " getConfigWiFiDisableInECBM " - + configWiFiDisableInECBM); - if ((msg.arg1 == 1) && configWiFiDisableInECBM) { + if (msg.arg1 == 1) { transitionTo(mEcmState); } break; @@ -598,8 +594,15 @@ public class WifiController extends StateMachine { private int mEcmEntryCount; @Override public void enter() { - mWifiStateMachinePrime.shutdownWifi(); - mWifiStateMachine.clearANQPCache(); + mWifiStateMachinePrime.stopSoftAPMode(); + boolean configWiFiDisableInECBM = + mFacade.getConfigWiFiDisableInECBM(mContext); + log("WifiController msg getConfigWiFiDisableInECBM " + + configWiFiDisableInECBM); + if (configWiFiDisableInECBM) { + mWifiStateMachinePrime.shutdownWifi(); + mWifiStateMachine.clearANQPCache(); + } mEcmEntryCount = 1; } diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java index 0e30af841..dbf730a73 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; @@ -204,7 +205,8 @@ public class WifiInjector { SystemProperties.get(BOOT_DEFAULT_WIFI_COUNTRY_CODE), mContext.getResources() .getBoolean(R.bool.config_wifi_revert_country_code_on_cellular_loss)); - mWifiApConfigStore = new WifiApConfigStore(mContext, mBackupManagerProxy); + mWifiApConfigStore = new WifiApConfigStore( + mContext, wifiStateMachineLooper, mBackupManagerProxy, mFrameworkFacade); // WifiConfigManager/Store objects and their dependencies. // New config store @@ -253,7 +255,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 +466,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 +478,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/WifiMetrics.java b/service/java/com/android/server/wifi/WifiMetrics.java index a3bd1697c..15f65c980 100644 --- a/service/java/com/android/server/wifi/WifiMetrics.java +++ b/service/java/com/android/server/wifi/WifiMetrics.java @@ -18,14 +18,18 @@ package com.android.server.wifi; import android.hardware.wifi.supplicant.V1_0.ISupplicantStaIfaceCallback; import android.net.NetworkAgent; +import android.net.wifi.EAPConstants; import android.net.wifi.ScanResult; import android.net.wifi.SupplicantState; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; +import android.net.wifi.hotspot2.PasspointConfiguration; import android.os.Handler; import android.os.Looper; import android.os.Message; +import android.os.SystemProperties; + import android.util.Base64; import android.util.Log; import android.util.Pair; @@ -175,6 +179,8 @@ public class WifiMetrics { private final SparseIntArray mAvailableSavedPasspointProviderBssidsInScanHistogram = new SparseIntArray(); + private final SparseIntArray mInstalledPasspointProfileType = new SparseIntArray(); + /** Mapping of "Connect to Network" notifications to counts. */ private final SparseIntArray mConnectToNetworkNotificationCount = new SparseIntArray(); /** Mapping of "Connect to Network" notification user actions to counts. */ @@ -2028,6 +2034,14 @@ public class WifiMetrics { + mWifiLogProto.numPasspointProviderUninstallSuccess); pw.println("mWifiLogProto.numPasspointProvidersSuccessfullyConnected=" + mWifiLogProto.numPasspointProvidersSuccessfullyConnected); + + pw.println("mWifiLogProto.installedPasspointProfileType: "); + for (int i = 0; i < mInstalledPasspointProfileType.size(); i++) { + int eapType = mInstalledPasspointProfileType.keyAt(i); + pw.println("EAP_METHOD (" + eapType + "): " + + mInstalledPasspointProfileType.valueAt(i)); + } + pw.println("mWifiLogProto.numRadioModeChangeToMcc=" + mWifiLogProto.numRadioModeChangeToMcc); pw.println("mWifiLogProto.numRadioModeChangeToScc=" @@ -2150,6 +2164,7 @@ public class WifiMetrics { pw.println("mWifiLogProto.isMacRandomizationOn=" + mIsMacRandomizationOn); pw.println("mWifiLogProto.scoreExperimentId=" + mWifiLogProto.scoreExperimentId); + pw.println("Hardware Version: " + SystemProperties.get("ro.boot.revision", "")); } } } @@ -2206,6 +2221,56 @@ public class WifiMetrics { } /** + * Update number of times for type of saved Passpoint profile. + * + * @param providers Passpoint providers installed on the device. + */ + public void updateSavedPasspointProfilesInfo( + Map<String, PasspointProvider> providers) { + int passpointType; + int eapType; + PasspointConfiguration config; + synchronized (mLock) { + mInstalledPasspointProfileType.clear(); + for (Map.Entry<String, PasspointProvider> entry : providers.entrySet()) { + config = entry.getValue().getConfig(); + if (config.getCredential().getUserCredential() != null) { + eapType = EAPConstants.EAP_TTLS; + } else if (config.getCredential().getCertCredential() != null) { + eapType = EAPConstants.EAP_TLS; + } else if (config.getCredential().getSimCredential() != null) { + eapType = config.getCredential().getSimCredential().getEapType(); + } else { + eapType = -1; + } + switch (eapType) { + case EAPConstants.EAP_TLS: + passpointType = WifiMetricsProto.PasspointProfileTypeCount.TYPE_EAP_TLS; + break; + case EAPConstants.EAP_TTLS: + passpointType = WifiMetricsProto.PasspointProfileTypeCount.TYPE_EAP_TTLS; + break; + case EAPConstants.EAP_SIM: + passpointType = WifiMetricsProto.PasspointProfileTypeCount.TYPE_EAP_SIM; + break; + case EAPConstants.EAP_AKA: + passpointType = WifiMetricsProto.PasspointProfileTypeCount.TYPE_EAP_AKA; + break; + case EAPConstants.EAP_AKA_PRIME: + passpointType = + WifiMetricsProto.PasspointProfileTypeCount.TYPE_EAP_AKA_PRIME; + break; + default: + passpointType = WifiMetricsProto.PasspointProfileTypeCount.TYPE_UNKNOWN; + + } + int count = mInstalledPasspointProfileType.get(passpointType); + mInstalledPasspointProfileType.put(passpointType, count + 1); + } + } + } + + /** * append the separate ConnectionEvent, SystemStateEntry and ScanReturnCode collections to their * respective lists within mWifiLogProto * @@ -2405,6 +2470,23 @@ public class WifiMetrics { keyVal.count = mConnectToNetworkNotificationActionCount.valueAt(i); notificationActionCountArray[i] = keyVal; } + + /** + * Convert the SparseIntArray of saved Passpoint profile types and counts to proto's + * repeated IntKeyVal array. + */ + int counts = mInstalledPasspointProfileType.size(); + mWifiLogProto.installedPasspointProfileType = + new WifiMetricsProto.PasspointProfileTypeCount[counts]; + for (int i = 0; i < counts; i++) { + mWifiLogProto.installedPasspointProfileType[i] = + new WifiMetricsProto.PasspointProfileTypeCount(); + mWifiLogProto.installedPasspointProfileType[i].eapMethodType = + mInstalledPasspointProfileType.keyAt(i); + mWifiLogProto.installedPasspointProfileType[i].count = + mInstalledPasspointProfileType.valueAt(i); + } + mWifiLogProto.connectToNetworkNotificationActionCount = notificationActionCountArray; mWifiLogProto.openNetworkRecommenderBlacklistSize = @@ -2449,6 +2531,7 @@ public class WifiMetrics { mWifiLogProto.wifiPowerStats = mWifiPowerMetrics.buildProto(); mWifiLogProto.wifiWakeStats = mWifiWakeMetrics.buildProto(); mWifiLogProto.isMacRandomizationOn = mIsMacRandomizationOn; + mWifiLogProto.hardwareRevision = SystemProperties.get("ro.boot.revision", ""); } } @@ -2529,6 +2612,7 @@ public class WifiMetrics { mWpsMetrics.clear(); mWifiWakeMetrics.clear(); mObserved80211mcApInScanHistogram.clear(); + mInstalledPasspointProfileType.clear(); } } 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/WifiServiceImpl.java b/service/java/com/android/server/wifi/WifiServiceImpl.java index a0bbded9d..6a8e0639b 100644 --- a/service/java/com/android/server/wifi/WifiServiceImpl.java +++ b/service/java/com/android/server/wifi/WifiServiceImpl.java @@ -1583,6 +1583,21 @@ public class WifiServiceImpl extends IWifiManager.Stub { } /** + * Method used to inform user of Ap Configuration conversion due to hardware. + */ + @Override + public void notifyUserOfApBandConversion(String packageName) { + enforceNetworkSettingsPermission(); + + if (mVerboseLoggingEnabled) { + mLog.info("notifyUserOfApBandConversion uid=% packageName=%") + .c(Binder.getCallingUid()).c(packageName).flush(); + } + + mWifiApConfigStore.notifyUserOfApBandConversion(packageName); + } + + /** * see {@link android.net.wifi.WifiManager#isScanAlwaysAvailable()} */ @Override @@ -2558,6 +2573,11 @@ public class WifiServiceImpl extends IWifiManager.Stub { wifiScoreReport.dump(fd, pw, args); } pw.println(); + SarManager sarManager = mWifiInjector.getSarManager(); + if (sarManager != null) { + sarManager.dump(fd, pw, args); + } + pw.println(); } } diff --git a/service/java/com/android/server/wifi/WifiVendorHal.java b/service/java/com/android/server/wifi/WifiVendorHal.java index 0d73459b5..fad90d140 100644 --- a/service/java/com/android/server/wifi/WifiVendorHal.java +++ b/service/java/com/android/server/wifi/WifiVendorHal.java @@ -201,7 +201,7 @@ public class WifiVendorHal { mVerboseLog.err("% returns %") .c(niceMethodName(trace, 3)) - .c(HexDump.dumpHexString(result)) + .c(result == null ? "(null)" : HexDump.dumpHexString(result)) .flush(); return result; @@ -2653,13 +2653,123 @@ 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 (in case voice call is supported), + * no backoff is needed */ + if (sarInfo.sarVoiceCallSupported) { + return sarInfo.isVoiceCall; + } else { + return false; + } + } + + /** + * 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 (if voice call is + * supported). + * Otherwise, an exception is thrown. + */ + private int frameworkToHalTxPowerScenario_1_1(SarInfo sarInfo) { + if (sarInfo.sarVoiceCallSupported && sarInfo.isVoiceCall) { + return android.hardware.wifi.V1_1.IWifiChip.TxPowerScenario.VOICE_CALL; + } else { + throw new IllegalArgumentException("bad scenario: voice call not active/supported"); + } + } + + /** + * 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 SAR sensor is supported, output only dependent on device proximity */ + if (sarInfo.sarSensorSupported) { + return (sarInfo.sensorState != SarInfo.SAR_SENSOR_FREE_SPACE); + } + if (sarInfo.sarSapSupported && sarInfo.isWifiSapEnabled) { + return true; + } + if (sarInfo.sarVoiceCallSupported && sarInfo.isVoiceCall) { + return true; + } + return false; + } - private int frameworkToHalTxPowerScenario(int scenario) { - switch (scenario) { - case WifiNative.TX_POWER_SCENARIO_VOICE_CALL: + /** + * 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 supported in this device, but SoftAP is, + * we make these assumptions: + * - All voice calls are treated as if device is near the head. + * - SoftAP scenario is treated as if device is near the body. + * In case neither SAR sensor, nor SoftAP is supported, 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.sarSensorSupported) { + switch(sarInfo.sensorState) { + case SarInfo.SAR_SENSOR_NEAR_BODY: + case SarInfo.SAR_SENSOR_NEAR_HAND: + if (sarInfo.isVoiceCall || sarInfo.isWifiSapEnabled) { + 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.isVoiceCall || sarInfo.isWifiSapEnabled) { + 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 if (sarInfo.sarSapSupported && sarInfo.sarVoiceCallSupported) { + if (sarInfo.isVoiceCall) { + return android.hardware.wifi.V1_2.IWifiChip + .TxPowerScenario.ON_HEAD_CELL_ON; + } else if (sarInfo.isWifiSapEnabled) { + return android.hardware.wifi.V1_2.IWifiChip + .TxPowerScenario.ON_BODY_CELL_ON; + } else { + throw new IllegalArgumentException("bad scenario: no voice call/softAP active"); + } + } else if (sarInfo.sarVoiceCallSupported) { + /* SAR Sensors and SoftAP not supported, act like V1_1 */ + if (sarInfo.isVoiceCall) { 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"); + } + } else { + throw new IllegalArgumentException("Invalid case: voice call not supported"); } } @@ -2667,35 +2777,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.d("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.d("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/service/java/com/android/server/wifi/hotspot2/PasspointManager.java b/service/java/com/android/server/wifi/hotspot2/PasspointManager.java index a0915fa52..4c2937cb5 100644 --- a/service/java/com/android/server/wifi/hotspot2/PasspointManager.java +++ b/service/java/com/android/server/wifi/hotspot2/PasspointManager.java @@ -656,6 +656,7 @@ public class PasspointManager { numConnectedProviders++; } } + mWifiMetrics.updateSavedPasspointProfilesInfo(mProviders); mWifiMetrics.updateSavedPasspointProfiles(numProviders, numConnectedProviders); } diff --git a/service/java/com/android/server/wifi/rtt/RttMetrics.java b/service/java/com/android/server/wifi/rtt/RttMetrics.java index c1bec8f97..ecc1c4812 100644 --- a/service/java/com/android/server/wifi/rtt/RttMetrics.java +++ b/service/java/com/android/server/wifi/rtt/RttMetrics.java @@ -116,6 +116,7 @@ public class RttMetrics { return "numCalls=" + numCalls + ", numIndividualCalls=" + numIndividualCalls + ", perUidInfo=" + perUidInfo + ", numRequestsHistogram=" + numRequestsHistogram + ", requestGapHistogram=" + requestGapHistogram + + ", statusHistogram=" + statusHistogram + ", measuredDistanceHistogram=" + measuredDistanceHistogram; } } diff --git a/service/java/com/android/server/wifi/rtt/RttNative.java b/service/java/com/android/server/wifi/rtt/RttNative.java index accd1a2f8..34fd2e3e7 100644 --- a/service/java/com/android/server/wifi/rtt/RttNative.java +++ b/service/java/com/android/server/wifi/rtt/RttNative.java @@ -166,6 +166,11 @@ public class RttNative extends IWifiRttControllerEventCallback.Stub { */ public boolean rangeRequest(int cmdId, RangingRequest request, boolean isCalledFromPrivilegedContext) { + return rangeRequestInternal(cmdId, request, isCalledFromPrivilegedContext, true); + } + + private boolean rangeRequestInternal(int cmdId, RangingRequest request, + boolean isCalledFromPrivilegedContext, boolean tryToReinitIfNecessary) { if (mDbg) { Log.v(TAG, "rangeRequest: cmdId=" + cmdId + ", # of requests=" + request.mRttPeers.size()); @@ -175,6 +180,11 @@ public class RttNative extends IWifiRttControllerEventCallback.Stub { synchronized (mLock) { if (!isReady()) { Log.e(TAG, "rangeRequest: RttController is null"); + if (tryToReinitIfNecessary) { + updateController(); + return rangeRequestInternal(cmdId, request, isCalledFromPrivilegedContext, + false); + } return false; } @@ -192,6 +202,14 @@ public class RttNative extends IWifiRttControllerEventCallback.Stub { try { WifiStatus status = mIWifiRttController.rangeRequest(cmdId, rttConfig); + if (status.code == WifiStatusCode.ERROR_WIFI_RTT_CONTROLLER_INVALID + && tryToReinitIfNecessary) { + Log.d(TAG, "rangeRequest: RTT controller invalidated from under us - reinit!"); + mIWifiRttController = null; + updateController(); + return rangeRequestInternal(cmdId, request, isCalledFromPrivilegedContext, + false); + } if (status.code != WifiStatusCode.SUCCESS) { Log.e(TAG, "rangeRequest: cannot issue range request -- code=" + status.code); return false; diff --git a/tests/wifitests/src/com/android/server/wifi/MockResources.java b/tests/wifitests/src/com/android/server/wifi/MockResources.java index ab40c6f8d..1a061e22a 100644 --- a/tests/wifitests/src/com/android/server/wifi/MockResources.java +++ b/tests/wifitests/src/com/android/server/wifi/MockResources.java @@ -23,11 +23,13 @@ public class MockResources extends android.test.mock.MockResources { private HashMap<Integer, Boolean> mBooleanValues; private HashMap<Integer, Integer> mIntegerValues; private HashMap<Integer, String> mStringValues; + private HashMap<Integer, CharSequence> mTextValues; public MockResources() { mBooleanValues = new HashMap<Integer, Boolean>(); mIntegerValues = new HashMap<Integer, Integer>(); mStringValues = new HashMap<Integer, String>(); + mTextValues = new HashMap<Integer, CharSequence>(); } @Override @@ -57,6 +59,15 @@ public class MockResources extends android.test.mock.MockResources { } } + @Override + public CharSequence getText(int id) { + if (mTextValues.containsKey(id)) { + return mTextValues.get(id); + } else { + return null; + } + } + public void setBoolean(int id, boolean value) { mBooleanValues.put(id, value); } @@ -68,4 +79,8 @@ public class MockResources extends android.test.mock.MockResources { public void setString(int id, String value) { mStringValues.put(id, value); } + + public void setText(int id, CharSequence value) { + mTextValues.put(id, value); + } } 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..2bf1f3ee4 --- /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(); + } + + @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.isWifiClientEnabled = 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.sensorState = 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.isWifiSapEnabled = true; + assertTrue(mSarInfo.shouldReport()); + mSarInfo.reportingSuccessful(); + + mSarInfo.sensorState = 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.isWifiScanOnlyEnabled = true; + mSarInfo.sensorState = 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.isWifiClientEnabled = true; + mSarInfo.sensorState = SarInfo.SAR_SENSOR_NEAR_HEAD; + assertTrue(mSarInfo.shouldReport()); + mSarInfo.reportingSuccessful(); + + mSarInfo.sensorState = SarInfo.SAR_SENSOR_NEAR_BODY; + assertTrue(mSarInfo.shouldReport()); + mSarInfo.reportingSuccessful(); + + mSarInfo.sensorState = 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.sensorState = SarInfo.SAR_SENSOR_NEAR_HEAD; + assertFalse(mSarInfo.shouldReport()); + + mSarInfo.isWifiClientEnabled = 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.isWifiClientEnabled = true; + assertTrue(mSarInfo.shouldReport()); + mSarInfo.reportingSuccessful(); + + mSarInfo.isVoiceCall = 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.isWifiClientEnabled = true; + assertTrue(mSarInfo.shouldReport()); + mSarInfo.reportingSuccessful(); + + mSarInfo.isWifiSapEnabled = 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.isWifiClientEnabled = true; + mSarInfo.sensorState = 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.isWifiClientEnabled = true; + mSarInfo.sensorState = SarInfo.SAR_SENSOR_NEAR_HEAD; + assertTrue(mSarInfo.shouldReport()); + mSarInfo.reportingSuccessful(); + + /* Changing the sensor state and fail to report */ + mSarInfo.sensorState = SarInfo.SAR_SENSOR_NEAR_BODY; + assertTrue(mSarInfo.shouldReport()); + + /* Changing the sensor back to the same value as last reported */ + mSarInfo.sensorState = 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..0cf9dd3f2 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,82 @@ 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 isSarSapEnabled, + boolean isSarSensorEnabled) { + mResources.setBoolean( + R.bool.config_wifi_framework_enable_sar_tx_power_limit, isSarEnabled); + mResources.setBoolean( + R.bool.config_wifi_framework_enable_soft_ap_sar_tx_power_limit, + isSarSapEnabled); 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,19 +203,90 @@ 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 */ + enableDebugLogs(); + } + + /** + * 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_soft_ap_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, false); verify(mTelephonyManager).listen(any(), eq(PhoneStateListener.LISTEN_CALL_STATE)); } @@ -144,7 +296,7 @@ public class SarManagerTest { */ @Test public void testSarMgr_disabledTxPowerScenario_registerPhone() throws Exception { - createSarManager(false); + createSarManager(false, false, false); verify(mTelephonyManager, never()).listen(any(), anyInt()); } @@ -153,27 +305,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, 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.isVoiceCall); /* 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.isVoiceCall); } /** @@ -181,66 +330,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, 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.isVoiceCall); } /** * 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, 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.isVoiceCall); /* 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.isVoiceCall); /* 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.isVoiceCall); + + /* 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, false); InOrder inOrder = inOrder(mWifiNative); @@ -251,6 +404,635 @@ 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, 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.isVoiceCall); + + /* Set phone state to OFFHOOK */ + mPhoneStateListener.onCallStateChanged(CALL_STATE_OFFHOOK, ""); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertTrue(mSarInfo.isVoiceCall); + + /* 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.isVoiceCall); + } + + /** + * 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, 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.isVoiceCall); + } + + /** + * 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, 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.isVoiceCall); + + /* 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, 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.isVoiceCall); + + /* Set phone state to OFFHOOK */ + mPhoneStateListener.onCallStateChanged(CALL_STATE_OFFHOOK, ""); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertTrue(mSarInfo.isVoiceCall); + + /* Set state back to ONHOOK */ + mPhoneStateListener.onCallStateChanged(CALL_STATE_IDLE, ""); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertFalse(mSarInfo.isVoiceCall); + } + + /** + * 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, 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.sensorState); + + /* Sensor event */ + sendSensorEvent(SAR_SENSOR_EVENT_BODY); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_BODY, mSarInfo.sensorState); + assertFalse(mSarInfo.isVoiceCall); + + /* Sensor event */ + sendSensorEvent(SAR_SENSOR_EVENT_HEAD); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HEAD, mSarInfo.sensorState); + assertFalse(mSarInfo.isVoiceCall); + + /* Sensor event */ + sendSensorEvent(SAR_SENSOR_EVENT_HAND); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HAND, mSarInfo.sensorState); + assertFalse(mSarInfo.isVoiceCall); + + /* Sensor event */ + sendSensorEvent(SAR_SENSOR_EVENT_FREE_SPACE); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_FREE_SPACE, mSarInfo.sensorState); + assertFalse(mSarInfo.isVoiceCall); + } + + /** + * 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, 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.sensorState); + assertFalse(mSarInfo.isVoiceCall); + + /* Start a Cell call */ + mPhoneStateListener.onCallStateChanged(CALL_STATE_OFFHOOK, ""); + inOrder.verify(mWifiNative).selectTxPowerScenario(any(SarInfo.class)); + assertEquals(SarInfo.SAR_SENSOR_FREE_SPACE, mSarInfo.sensorState); + assertTrue(mSarInfo.isVoiceCall); + + /* Sensor event */ + sendSensorEvent(SAR_SENSOR_EVENT_BODY); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_BODY, mSarInfo.sensorState); + assertTrue(mSarInfo.isVoiceCall); + + /* Sensor event */ + sendSensorEvent(SAR_SENSOR_EVENT_HEAD); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HEAD, mSarInfo.sensorState); + assertTrue(mSarInfo.isVoiceCall); + + /* Sensor event */ + sendSensorEvent(SAR_SENSOR_EVENT_HAND); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HAND, mSarInfo.sensorState); + assertTrue(mSarInfo.isVoiceCall); + + /* Sensor event */ + sendSensorEvent(SAR_SENSOR_EVENT_FREE_SPACE); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_FREE_SPACE, mSarInfo.sensorState); + assertTrue(mSarInfo.isVoiceCall); + } + + /** + * 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, 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.sensorState); + assertFalse(mSarInfo.isVoiceCall); + + /* Sensor event */ + sendSensorEvent(SAR_SENSOR_EVENT_HEAD); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HEAD, mSarInfo.sensorState); + assertFalse(mSarInfo.isVoiceCall); + + + /* Start a Cell call */ + mPhoneStateListener.onCallStateChanged(CALL_STATE_OFFHOOK, ""); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HEAD, mSarInfo.sensorState); + assertTrue(mSarInfo.isVoiceCall); + + /* End a Cell call */ + mPhoneStateListener.onCallStateChanged(CALL_STATE_IDLE, ""); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HEAD, mSarInfo.sensorState); + assertFalse(mSarInfo.isVoiceCall); + } + + /** + * 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, 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.sensorState); + assertFalse(mSarInfo.isVoiceCall); + } + + /** + * 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.sensorState); + assertFalse(mSarInfo.isVoiceCall); + + /* Start a Cell Call */ + mPhoneStateListener.onCallStateChanged(CALL_STATE_OFFHOOK, ""); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HEAD, mSarInfo.sensorState); + assertTrue(mSarInfo.isVoiceCall); + + /* End the call */ + mPhoneStateListener.onCallStateChanged(CALL_STATE_IDLE, ""); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HEAD, mSarInfo.sensorState); + assertFalse(mSarInfo.isVoiceCall); + } + + /** + * 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.sensorState); + assertFalse(mSarInfo.isVoiceCall); + + /* Start a Cell Call */ + mPhoneStateListener.onCallStateChanged(CALL_STATE_OFFHOOK, ""); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HEAD, mSarInfo.sensorState); + assertTrue(mSarInfo.isVoiceCall); + + /* End the call */ + mPhoneStateListener.onCallStateChanged(CALL_STATE_IDLE, ""); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HEAD, mSarInfo.sensorState); + assertFalse(mSarInfo.isVoiceCall); + } + + /** + * 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.sensorState); + assertFalse(mSarInfo.isVoiceCall); + + /* Start a Cell Call */ + mPhoneStateListener.onCallStateChanged(CALL_STATE_OFFHOOK, ""); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HEAD, mSarInfo.sensorState); + assertTrue(mSarInfo.isVoiceCall); + + /* End the call */ + mPhoneStateListener.onCallStateChanged(CALL_STATE_IDLE, ""); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HEAD, mSarInfo.sensorState); + assertFalse(mSarInfo.isVoiceCall); + } + + /** + * 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, 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, 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.isVoiceCall); + assertTrue(mSarInfo.isWifiSapEnabled); + assertFalse(mSarInfo.isWifiScanOnlyEnabled); + } + + /** + * 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, 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.sensorState); + assertFalse(mSarInfo.isWifiSapEnabled); + + /* Enable WiFi SoftAP State */ + mSarMgr.setSapWifiState(WifiManager.WIFI_AP_STATE_ENABLED); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HEAD, mSarInfo.sensorState); + assertTrue(mSarInfo.isWifiSapEnabled); + + /* Disable Wifi SoftAP state */ + mSarMgr.setSapWifiState(WifiManager.WIFI_AP_STATE_DISABLED); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HEAD, mSarInfo.sensorState); + assertFalse(mSarInfo.isWifiSapEnabled); + } + + /** + * 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, 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.sensorState); + assertTrue(mSarInfo.isWifiSapEnabled); + + /* 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.sensorState); + assertFalse(mSarInfo.isWifiSapEnabled); + } + + /** + * 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, false); + + /* Enable Wifi ScanOnly State */ + mSarMgr.setScanOnlyWifiState(WifiManager.WIFI_STATE_ENABLED); + captureSarInfo(mWifiNative); + + verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertFalse(mSarInfo.isWifiSapEnabled); + assertTrue(mSarInfo.isWifiScanOnlyEnabled); + } + + /** + * 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, 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.isWifiSapEnabled); + assertTrue(mSarInfo.isWifiScanOnlyEnabled); + + /* 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, false); + + InOrder inOrder = inOrder(mWifiNative); + + /* Enable WiFi State */ + mSarMgr.setClientWifiState(WifiManager.WIFI_STATE_ENABLED); + captureSarInfo(mWifiNative); + + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertFalse(mSarInfo.isVoiceCall); + assertFalse(mSarInfo.isWifiSapEnabled); + assertFalse(mSarInfo.isWifiScanOnlyEnabled); + + /* Enable SoftAP state */ + mSarMgr.setSapWifiState(WifiManager.WIFI_AP_STATE_ENABLED); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertFalse(mSarInfo.isVoiceCall); + assertTrue(mSarInfo.isWifiSapEnabled); + assertFalse(mSarInfo.isWifiScanOnlyEnabled); + + /* 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.isVoiceCall); + assertFalse(mSarInfo.isWifiSapEnabled); + assertTrue(mSarInfo.isWifiScanOnlyEnabled); + } + + /** + * 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, 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.isVoiceCall); + assertFalse(mSarInfo.isWifiSapEnabled); + assertFalse(mSarInfo.isWifiScanOnlyEnabled); + + /* Enable SoftAP state */ + mSarMgr.setSapWifiState(WifiManager.WIFI_AP_STATE_ENABLED); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertFalse(mSarInfo.isVoiceCall); + assertTrue(mSarInfo.isWifiSapEnabled); + assertFalse(mSarInfo.isWifiScanOnlyEnabled); + + /* Disable WiFi State, reporting should still happen */ + mSarMgr.setClientWifiState(WifiManager.WIFI_STATE_DISABLED); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertFalse(mSarInfo.isVoiceCall); + assertTrue(mSarInfo.isWifiSapEnabled); + assertFalse(mSarInfo.isWifiScanOnlyEnabled); + assertFalse(mSarInfo.isWifiClientEnabled); + + /* Enable ScanOnly state */ + mSarMgr.setScanOnlyWifiState(WifiManager.WIFI_STATE_ENABLED); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertFalse(mSarInfo.isVoiceCall); + assertTrue(mSarInfo.isWifiSapEnabled); + assertTrue(mSarInfo.isWifiScanOnlyEnabled); + assertFalse(mSarInfo.isWifiClientEnabled); + + /* Disable SoftAP state */ + mSarMgr.setSapWifiState(WifiManager.WIFI_AP_STATE_DISABLED); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertFalse(mSarInfo.isVoiceCall); + assertFalse(mSarInfo.isWifiSapEnabled); + assertTrue(mSarInfo.isWifiScanOnlyEnabled); + assertFalse(mSarInfo.isWifiClientEnabled); } } 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..21c3797b3 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 = @@ -113,6 +114,9 @@ public class SoftApManagerTest { when(mContext.getResources()).thenReturn(mResources); when(mResources.getInteger(R.integer.config_wifi_framework_soft_ap_timeout_delay)) .thenReturn(600000); + when(mWifiNative.setCountryCodeHal( + TEST_INTERFACE_NAME, TEST_COUNTRY_CODE.toUpperCase(Locale.ROOT))) + .thenReturn(true); } private WifiConfiguration createDefaultApConfig() { @@ -121,7 +125,7 @@ public class SoftApManagerTest { return defaultConfig; } - private SoftApManager createSoftApManager(SoftApModeConfiguration config) throws Exception { + private SoftApManager createSoftApManager(SoftApModeConfiguration config, String countryCode) { if (config.getWifiConfiguration() == null) { when(mWifiApConfigStore.getApConfiguration()).thenReturn(mDefaultApConfig); } @@ -129,11 +133,12 @@ public class SoftApManagerTest { mLooper.getLooper(), mFrameworkFacade, mWifiNative, - TEST_COUNTRY_CODE, + countryCode, mCallback, mWifiApConfigStore, config, - mWifiMetrics); + mWifiMetrics, + mSarManager); mLooper.dispatchAll(); return newSoftApManager; @@ -205,7 +210,8 @@ public class SoftApManagerTest { mCallback, mWifiApConfigStore, nullApConfig, - mWifiMetrics); + mWifiMetrics, + mSarManager); mLooper.dispatchAll(); newSoftApManager.start(); mLooper.dispatchAll(); @@ -247,7 +253,8 @@ public class SoftApManagerTest { mCallback, mWifiApConfigStore, nullApConfig, - mWifiMetrics); + mWifiMetrics, + mSarManager); mLooper.dispatchAll(); newSoftApManager.start(); mLooper.dispatchAll(); @@ -288,7 +295,8 @@ public class SoftApManagerTest { mCallback, mWifiApConfigStore, nullApConfig, - mWifiMetrics); + mWifiMetrics, + mSarManager); mLooper.dispatchAll(); newSoftApManager.start(); mLooper.dispatchAll(); @@ -307,11 +315,54 @@ public class SoftApManagerTest { } /** + * Tests that the generic error is propagated and properly reported when starting softap and no + * country code is provided. + */ + @Test + public void startSoftApOn5GhzFailGeneralErrorForNoCountryCode() throws Exception { + WifiConfiguration config = new WifiConfiguration(); + config.apBand = WifiConfiguration.AP_BAND_5GHZ; + config.SSID = TEST_SSID; + SoftApModeConfiguration softApConfig = + new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, config); + + when(mWifiNative.setupInterfaceForSoftApMode(any())).thenReturn(TEST_INTERFACE_NAME); + + SoftApManager newSoftApManager = new SoftApManager(mContext, + mLooper.getLooper(), + mFrameworkFacade, + mWifiNative, + null, + mCallback, + mWifiApConfigStore, + softApConfig, + mWifiMetrics, + mSarManager); + mLooper.dispatchAll(); + newSoftApManager.start(); + mLooper.dispatchAll(); + + verify(mWifiNative, never()).setCountryCodeHal(eq(TEST_INTERFACE_NAME), any()); + + ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); + verify(mContext, times(2)).sendStickyBroadcastAsUser(intentCaptor.capture(), + eq(UserHandle.ALL)); + + List<Intent> capturedIntents = intentCaptor.getAllValues(); + checkApStateChangedBroadcast(capturedIntents.get(0), WIFI_AP_STATE_ENABLING, + WIFI_AP_STATE_DISABLED, HOTSPOT_NO_ERROR, TEST_INTERFACE_NAME, + softApConfig.getTargetMode()); + checkApStateChangedBroadcast(capturedIntents.get(1), WIFI_AP_STATE_FAILED, + WIFI_AP_STATE_ENABLING, WifiManager.SAP_START_FAILURE_GENERAL, TEST_INTERFACE_NAME, + softApConfig.getTargetMode()); + } + + /** * Tests that the generic error is propagated and properly reported when starting softap and the - * specified channel cannot be used. + * country code cannot be set. */ @Test - public void startSoftApFailGeneralErrorForConfigChannel() throws Exception { + public void startSoftApOn5GhzFailGeneralErrorForCountryCodeSetFailure() throws Exception { WifiConfiguration config = new WifiConfiguration(); config.apBand = WifiConfiguration.AP_BAND_5GHZ; config.SSID = TEST_SSID; @@ -319,21 +370,27 @@ public class SoftApManagerTest { new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, config); when(mWifiNative.setupInterfaceForSoftApMode(any())).thenReturn(TEST_INTERFACE_NAME); - when(mWifiNative.setCountryCodeHal(eq(TEST_INTERFACE_NAME), any())).thenReturn(false); + when(mWifiNative.setCountryCodeHal( + TEST_INTERFACE_NAME, TEST_COUNTRY_CODE.toUpperCase(Locale.ROOT))) + .thenReturn(false); SoftApManager newSoftApManager = new SoftApManager(mContext, mLooper.getLooper(), mFrameworkFacade, mWifiNative, - "US", + TEST_COUNTRY_CODE, mCallback, mWifiApConfigStore, softApConfig, - mWifiMetrics); + mWifiMetrics, + mSarManager); mLooper.dispatchAll(); newSoftApManager.start(); mLooper.dispatchAll(); + verify(mWifiNative).setCountryCodeHal( + TEST_INTERFACE_NAME, TEST_COUNTRY_CODE.toUpperCase(Locale.ROOT)); + ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); verify(mContext, times(2)).sendStickyBroadcastAsUser(intentCaptor.capture(), eq(UserHandle.ALL)); @@ -348,6 +405,76 @@ public class SoftApManagerTest { } /** + * Tests that there is no failure in starting softap in 2Ghz band when no country code is + * provided. + */ + @Test + public void startSoftApOn24GhzNoFailForNoCountryCode() throws Exception { + WifiConfiguration config = new WifiConfiguration(); + config.apBand = WifiConfiguration.AP_BAND_2GHZ; + config.SSID = TEST_SSID; + SoftApModeConfiguration softApConfig = + new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, config); + + startSoftApAndVerifyEnabled(softApConfig, null); + verify(mWifiNative, never()).setCountryCodeHal(eq(TEST_INTERFACE_NAME), any()); + } + + /** + * Tests that there is no failure in starting softap in ANY band when no country code is + * provided. + */ + @Test + public void startSoftApOnAnyGhzNoFailForNoCountryCode() throws Exception { + WifiConfiguration config = new WifiConfiguration(); + config.apBand = WifiConfiguration.AP_BAND_ANY; + config.SSID = TEST_SSID; + SoftApModeConfiguration softApConfig = + new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, config); + + startSoftApAndVerifyEnabled(softApConfig, null); + verify(mWifiNative, never()).setCountryCodeHal(eq(TEST_INTERFACE_NAME), any()); + } + + /** + * Tests that there is no failure in starting softap in 2Ghz band when country code cannot be + * set. + */ + @Test + public void startSoftApOn2GhzNoFailForCountryCodeSetFailure() throws Exception { + WifiConfiguration config = new WifiConfiguration(); + config.apBand = WifiConfiguration.AP_BAND_2GHZ; + config.SSID = TEST_SSID; + SoftApModeConfiguration softApConfig = + new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, config); + + when(mWifiNative.setCountryCodeHal(eq(TEST_INTERFACE_NAME), any())).thenReturn(false); + + startSoftApAndVerifyEnabled(softApConfig, TEST_COUNTRY_CODE); + verify(mWifiNative).setCountryCodeHal( + TEST_INTERFACE_NAME, TEST_COUNTRY_CODE.toUpperCase(Locale.ROOT)); + } + + /** + * Tests that there is no failure in starting softap in ANY band when country code cannot be + * set. + */ + @Test + public void startSoftApOnAnyNoFailForCountryCodeSetFailure() throws Exception { + WifiConfiguration config = new WifiConfiguration(); + config.apBand = WifiConfiguration.AP_BAND_ANY; + config.SSID = TEST_SSID; + SoftApModeConfiguration softApConfig = + new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, config); + + when(mWifiNative.setCountryCodeHal(eq(TEST_INTERFACE_NAME), any())).thenReturn(false); + + startSoftApAndVerifyEnabled(softApConfig, TEST_COUNTRY_CODE); + verify(mWifiNative).setCountryCodeHal( + TEST_INTERFACE_NAME, TEST_COUNTRY_CODE.toUpperCase(Locale.ROOT)); + } + + /** * Tests that the NO_CHANNEL error is propagated and properly reported when starting softap and * a valid channel cannot be determined. */ @@ -371,7 +498,8 @@ public class SoftApManagerTest { mCallback, mWifiApConfigStore, softApConfig, - mWifiMetrics); + mWifiMetrics, + mSarManager); mLooper.dispatchAll(); newSoftApManager.start(); mLooper.dispatchAll(); @@ -408,7 +536,8 @@ public class SoftApManagerTest { mCallback, mWifiApConfigStore, softApModeConfig, - mWifiMetrics); + mWifiMetrics, + mSarManager); mLooper.dispatchAll(); newSoftApManager.start(); @@ -424,11 +553,13 @@ public class SoftApManagerTest { @Test public void stopWhenNotStarted() throws Exception { mSoftApManager = createSoftApManager( - new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null)); + new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null), + TEST_COUNTRY_CODE); mSoftApManager.stop(); 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 +590,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, @@ -901,14 +1033,16 @@ public class SoftApManagerTest { /** Starts soft AP and verifies that it is enabled successfully. */ protected void startSoftApAndVerifyEnabled( SoftApModeConfiguration softApConfig) throws Exception { + startSoftApAndVerifyEnabled(softApConfig, TEST_COUNTRY_CODE); + } + + /** Starts soft AP and verifies that it is enabled successfully. */ + protected void startSoftApAndVerifyEnabled( + SoftApModeConfiguration softApConfig, String countryCode) throws Exception { WifiConfiguration expectedConfig; InOrder order = inOrder(mCallback, mWifiNative); - when(mWifiNative.setCountryCodeHal( - TEST_INTERFACE_NAME, TEST_COUNTRY_CODE.toUpperCase(Locale.ROOT))) - .thenReturn(true); - - mSoftApManager = createSoftApManager(softApConfig); + mSoftApManager = createSoftApManager(softApConfig, countryCode); WifiConfiguration config = softApConfig.getWifiConfiguration(); if (config == null) { when(mWifiApConfigStore.getApConfiguration()).thenReturn(mDefaultApConfig); @@ -938,6 +1072,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/WifiApConfigStoreTest.java b/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java index 1672dca58..88312ce4c 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java @@ -19,25 +19,38 @@ package com.android.server.wifi; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.app.Notification; +import android.app.NotificationManager; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiConfiguration.KeyMgmt; +import android.os.Build; +import android.os.test.TestLooper; import android.support.test.filters.SmallTest; import com.android.internal.R; +import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.io.File; import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Random; /** @@ -54,19 +67,34 @@ public class WifiApConfigStoreTest { private static final String TEST_CONFIGURED_AP_SSID = "ConfiguredAP"; private static final String TEST_DEFAULT_HOTSPOT_SSID = "TestShare"; private static final String TEST_DEFAULT_HOTSPOT_PSK = "TestPassword"; + private static final String TEST_APCONFIG_CHANGE_NOTIFICATION_TITLE = "Notification title"; + private static final String TEST_APCONFIG_CHANGE_NOTIFICATION_SUMMARY = "Notification summary"; + private static final String TEST_APCONFIG_CHANGE_NOTIFICATION_DETAILED = + "Notification detailed"; private static final int RAND_SSID_INT_MIN = 1000; private static final int RAND_SSID_INT_MAX = 9999; private static final String TEST_CHAR_SET_AS_STRING = "abcdefghijklmnopqrstuvwxyz0123456789"; - @Mock Context mContext; - @Mock BackupManagerProxy mBackupManagerProxy; - File mApConfigFile; - Random mRandom; - MockResources mResources; + @Mock private Context mContext; + private TestLooper mLooper; + @Mock private BackupManagerProxy mBackupManagerProxy; + @Mock private FrameworkFacade mFrameworkFacade; + private File mApConfigFile; + private Random mRandom; + private MockResources mResources; + @Mock private ApplicationInfo mMockApplInfo; + private BroadcastReceiver mBroadcastReceiver; + @Mock private NotificationManager mNotificationManager; + private ArrayList<Integer> mKnownGood2GChannelList; @Before public void setUp() throws Exception { + mLooper = new TestLooper(); MockitoAnnotations.initMocks(this); + when(mContext.getSystemService(Context.NOTIFICATION_SERVICE)) + .thenReturn(mNotificationManager); + mMockApplInfo.targetSdkVersion = Build.VERSION_CODES.P; + when(mContext.getApplicationInfo()).thenReturn(mMockApplInfo); /* Create a temporary file for AP config file storage. */ mApConfigFile = File.createTempFile(TEST_AP_CONFIG_FILE_PREFIX, ""); @@ -74,15 +102,24 @@ public class WifiApConfigStoreTest { /* Setup expectations for Resources to return some default settings. */ mResources = new MockResources(); mResources.setString(R.string.config_wifi_framework_sap_2G_channel_list, - TEST_DEFAULT_2G_CHANNEL_LIST); + TEST_DEFAULT_2G_CHANNEL_LIST); mResources.setString(R.string.wifi_tether_configure_ssid_default, - TEST_DEFAULT_AP_SSID); + TEST_DEFAULT_AP_SSID); mResources.setString(R.string.wifi_localhotspot_configure_ssid_default, - TEST_DEFAULT_HOTSPOT_SSID); + TEST_DEFAULT_HOTSPOT_SSID); /* Default to device that does not require ap band conversion */ mResources.setBoolean(R.bool.config_wifi_convert_apband_5ghz_to_any, false); + mResources.setText(R.string.wifi_softap_config_change, + TEST_APCONFIG_CHANGE_NOTIFICATION_TITLE); + mResources.setText(R.string.wifi_softap_config_change_summary, + TEST_APCONFIG_CHANGE_NOTIFICATION_SUMMARY); + mResources.setText(R.string.wifi_softap_config_change_detailed, + TEST_APCONFIG_CHANGE_NOTIFICATION_DETAILED); when(mContext.getResources()).thenReturn(mResources); + // build the known good 2G channel list: TEST_DEFAULT_2G_CHANNEL_LIST + mKnownGood2GChannelList = new ArrayList(Arrays.asList(1, 2, 3, 4, 5, 6)); + mRandom = new Random(); } @@ -93,6 +130,22 @@ public class WifiApConfigStoreTest { } /** + * Helper method to create and verify actions for the ApConfigStore used in the following tests. + */ + private WifiApConfigStore createWifiApConfigStore() { + WifiApConfigStore store = new WifiApConfigStore( + mContext, mLooper.getLooper(), mBackupManagerProxy, mFrameworkFacade, + mApConfigFile.getPath()); + + ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor = + ArgumentCaptor.forClass(BroadcastReceiver.class); + verify(mContext).registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any()); + mBroadcastReceiver = broadcastReceiverCaptor.getValue(); + + return store; + } + + /** * Generate a WifiConfiguration based on the specified parameters. */ private WifiConfiguration setupApConfig( @@ -149,7 +202,8 @@ public class WifiApConfigStoreTest { @Test public void initWithDefaultConfiguration() throws Exception { WifiApConfigStore store = new WifiApConfigStore( - mContext, mBackupManagerProxy, mApConfigFile.getPath()); + mContext, mLooper.getLooper(), mBackupManagerProxy, mFrameworkFacade, + mApConfigFile.getPath()); verifyDefaultApConfig(store.getApConfiguration(), TEST_DEFAULT_AP_SSID); } @@ -167,7 +221,8 @@ public class WifiApConfigStoreTest { 40 /* AP channel */); writeApConfigFile(expectedConfig); WifiApConfigStore store = new WifiApConfigStore( - mContext, mBackupManagerProxy, mApConfigFile.getPath()); + mContext, mLooper.getLooper(), mBackupManagerProxy, mFrameworkFacade, + mApConfigFile.getPath()); verifyApConfig(expectedConfig, store.getApConfiguration()); } @@ -187,7 +242,8 @@ public class WifiApConfigStoreTest { 40 /* AP channel */); writeApConfigFile(expectedConfig); WifiApConfigStore store = new WifiApConfigStore( - mContext, mBackupManagerProxy, mApConfigFile.getPath()); + mContext, mLooper.getLooper(), mBackupManagerProxy, mFrameworkFacade, + mApConfigFile.getPath()); verifyApConfig(expectedConfig, store.getApConfiguration()); store.setApConfiguration(null); @@ -202,7 +258,8 @@ public class WifiApConfigStoreTest { public void updateApConfiguration() throws Exception { /* Initialize WifiApConfigStore with default configuration. */ WifiApConfigStore store = new WifiApConfigStore( - mContext, mBackupManagerProxy, mApConfigFile.getPath()); + mContext, mLooper.getLooper(), mBackupManagerProxy, mFrameworkFacade, + mApConfigFile.getPath()); verifyDefaultApConfig(store.getApConfiguration(), TEST_DEFAULT_AP_SSID); /* Update with a valid configuration. */ @@ -226,7 +283,8 @@ public class WifiApConfigStoreTest { public void convertSingleModeDeviceAnyTo5Ghz() throws Exception { /* Initialize WifiApConfigStore with default configuration. */ WifiApConfigStore store = new WifiApConfigStore( - mContext, mBackupManagerProxy, mApConfigFile.getPath()); + mContext, mLooper.getLooper(), mBackupManagerProxy, mFrameworkFacade, + mApConfigFile.getPath()); verifyDefaultApConfig(store.getApConfiguration(), TEST_DEFAULT_AP_SSID); /* Update with a valid configuration. */ @@ -257,7 +315,8 @@ public class WifiApConfigStoreTest { public void singleModeDevice5GhzNotConverted() throws Exception { /* Initialize WifiApConfigStore with default configuration. */ WifiApConfigStore store = new WifiApConfigStore( - mContext, mBackupManagerProxy, mApConfigFile.getPath()); + mContext, mLooper.getLooper(), mBackupManagerProxy, mFrameworkFacade, + mApConfigFile.getPath()); verifyDefaultApConfig(store.getApConfiguration(), TEST_DEFAULT_AP_SSID); /* Update with a valid configuration. */ @@ -282,7 +341,8 @@ public class WifiApConfigStoreTest { /* Initialize WifiApConfigStore with default configuration. */ WifiApConfigStore store = new WifiApConfigStore( - mContext, mBackupManagerProxy, mApConfigFile.getPath()); + mContext, mLooper.getLooper(), mBackupManagerProxy, mFrameworkFacade, + mApConfigFile.getPath()); verifyDefaultApConfig(store.getApConfiguration(), TEST_DEFAULT_AP_SSID); /* Update with a valid configuration. */ @@ -315,7 +375,8 @@ public class WifiApConfigStoreTest { /* Initialize WifiApConfigStore with default configuration. */ WifiApConfigStore store = new WifiApConfigStore( - mContext, mBackupManagerProxy, mApConfigFile.getPath()); + mContext, mLooper.getLooper(), mBackupManagerProxy, mFrameworkFacade, + mApConfigFile.getPath()); verifyDefaultApConfig(store.getApConfiguration(), TEST_DEFAULT_AP_SSID); /* Update with a valid configuration. */ @@ -353,7 +414,8 @@ public class WifiApConfigStoreTest { writeApConfigFile(persistedConfig); WifiApConfigStore store = new WifiApConfigStore( - mContext, mBackupManagerProxy, mApConfigFile.getPath()); + mContext, mLooper.getLooper(), mBackupManagerProxy, mFrameworkFacade, + mApConfigFile.getPath()); verifyApConfig(expectedConfig, store.getApConfiguration()); verify(mBackupManagerProxy).notifyDataChanged(); } @@ -374,7 +436,8 @@ public class WifiApConfigStoreTest { writeApConfigFile(persistedConfig); WifiApConfigStore store = new WifiApConfigStore( - mContext, mBackupManagerProxy, mApConfigFile.getPath()); + mContext, mLooper.getLooper(), mBackupManagerProxy, mFrameworkFacade, + mApConfigFile.getPath()); verifyApConfig(persistedConfig, store.getApConfiguration()); verify(mBackupManagerProxy, never()).notifyDataChanged(); } @@ -403,7 +466,8 @@ public class WifiApConfigStoreTest { writeApConfigFile(persistedConfig); WifiApConfigStore store = new WifiApConfigStore( - mContext, mBackupManagerProxy, mApConfigFile.getPath()); + mContext, mLooper.getLooper(), mBackupManagerProxy, mFrameworkFacade, + mApConfigFile.getPath()); verifyApConfig(expectedConfig, store.getApConfiguration()); verify(mBackupManagerProxy).notifyDataChanged(); } @@ -426,7 +490,8 @@ public class WifiApConfigStoreTest { writeApConfigFile(persistedConfig); WifiApConfigStore store = new WifiApConfigStore( - mContext, mBackupManagerProxy, mApConfigFile.getPath()); + mContext, mLooper.getLooper(), mBackupManagerProxy, mFrameworkFacade, + mApConfigFile.getPath()); verifyApConfig(persistedConfig, store.getApConfiguration()); verify(mBackupManagerProxy, never()).notifyDataChanged(); } @@ -437,7 +502,8 @@ public class WifiApConfigStoreTest { @Test public void getDefaultApConfigurationIsValid() { WifiApConfigStore store = new WifiApConfigStore( - mContext, mBackupManagerProxy, mApConfigFile.getPath()); + mContext, mLooper.getLooper(), mBackupManagerProxy, mFrameworkFacade, + mApConfigFile.getPath()); WifiConfiguration config = store.getApConfiguration(); assertTrue(WifiApConfigStore.validateApWifiConfiguration(config)); } @@ -597,4 +663,51 @@ public class WifiApConfigStoreTest { config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); assertFalse(WifiApConfigStore.validateApWifiConfiguration(config)); } + + /** + * Verify the default 2GHz channel list is properly returned. + */ + @Test + public void testDefault2GHzChannelListReturned() { + // first build known good list + WifiApConfigStore store = createWifiApConfigStore(); + ArrayList<Integer> channels = store.getAllowed2GChannel(); + + assertEquals(mKnownGood2GChannelList.size(), channels.size()); + for (int channel : channels) { + assertTrue(mKnownGood2GChannelList.contains(channel)); + } + } + + /** + * Verify a notification is posted when triggered when the ap config was converted. + */ + @Test + public void testNotifyUserOfApBandConversion() throws Exception { + WifiApConfigStore store = createWifiApConfigStore(); + store.notifyUserOfApBandConversion(TAG); + // verify the notification is posted + ArgumentCaptor<Notification> notificationCaptor = + ArgumentCaptor.forClass(Notification.class); + verify(mNotificationManager).notify(eq(SystemMessage.NOTE_SOFTAP_CONFIG_CHANGED), + notificationCaptor.capture()); + Notification notification = notificationCaptor.getValue(); + assertEquals(TEST_APCONFIG_CHANGE_NOTIFICATION_TITLE, + notification.extras.getCharSequence(Notification.EXTRA_TITLE)); + assertEquals(TEST_APCONFIG_CHANGE_NOTIFICATION_DETAILED, + notification.extras.getCharSequence(Notification.EXTRA_BIG_TEXT)); + assertEquals(TEST_APCONFIG_CHANGE_NOTIFICATION_SUMMARY, + notification.extras.getCharSequence(Notification.EXTRA_SUMMARY_TEXT)); + } + + /** + * Verify the posted notification is cleared when the user interacts with it. + */ + @Test + public void testNotificationClearedWhenContentIsTapped() throws Exception { + WifiApConfigStore store = createWifiApConfigStore(); + Intent intent = new Intent(WifiApConfigStore.ACTION_HOTSPOT_CONFIG_USER_TAPPED_CONTENT); + mBroadcastReceiver.onReceive(mContext, intent); + verify(mNotificationManager).cancel(eq(SystemMessage.NOTE_SOFTAP_CONFIG_CHANGED)); + } } diff --git a/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java b/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java index 2a60a1096..5646efc62 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java @@ -18,21 +18,26 @@ package com.android.server.wifi; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import android.net.NetworkAgent; +import android.net.wifi.EAPConstants; import android.net.wifi.ScanResult; import android.net.wifi.SupplicantState; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiManager; import android.net.wifi.WifiSsid; +import android.net.wifi.hotspot2.PasspointConfiguration; +import android.net.wifi.hotspot2.pps.Credential; import android.os.Handler; import android.os.test.TestLooper; import android.support.test.filters.SmallTest; import android.util.Base64; import android.util.Pair; +import android.util.SparseIntArray; import com.android.server.wifi.aware.WifiAwareMetrics; import com.android.server.wifi.hotspot2.NetworkDetail; @@ -41,6 +46,7 @@ import com.android.server.wifi.hotspot2.PasspointMatch; import com.android.server.wifi.hotspot2.PasspointProvider; import com.android.server.wifi.nano.WifiMetricsProto; import com.android.server.wifi.nano.WifiMetricsProto.ConnectToNetworkNotificationAndActionCount; +import com.android.server.wifi.nano.WifiMetricsProto.PasspointProfileTypeCount; import com.android.server.wifi.nano.WifiMetricsProto.PnoScanMetrics; import com.android.server.wifi.nano.WifiMetricsProto.SoftApConnectedClientsEvent; import com.android.server.wifi.nano.WifiMetricsProto.StaEvent; @@ -58,7 +64,9 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; import java.util.BitSet; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -269,12 +277,26 @@ public class WifiMetricsTest { private static final int NUM_SOFTAP_ON_FAILURE_DUE_TO_HOSTAPD = 31; private static final int NUM_SOFTAP_INTERFACE_DOWN = 65; private static final int NUM_CLIENT_INTERFACE_DOWN = 12; - private static final int NUM_PASSPOINT_PROVIDERS = 4; + private static final int NUM_PASSPOINT_PROVIDERS = 7; private static final int NUM_PASSPOINT_PROVIDER_INSTALLATION = 5; private static final int NUM_PASSPOINT_PROVIDER_INSTALL_SUCCESS = 4; private static final int NUM_PASSPOINT_PROVIDER_UNINSTALLATION = 3; private static final int NUM_PASSPOINT_PROVIDER_UNINSTALL_SUCCESS = 2; private static final int NUM_PASSPOINT_PROVIDERS_SUCCESSFULLY_CONNECTED = 1; + private static final int NUM_EAP_SIM_TYPE = 1; + private static final int NUM_EAP_TTLS_TYPE = 2; + private static final int NUM_EAP_TLS_TYPE = 3; + private static final int NUM_EAP_AKA_TYPE = 4; + private static final int NUM_EAP_AKA_PRIME_TYPE = 5; + private static final SparseIntArray SAVED_PASSPOINT_PROVIDERS_TYPE = new SparseIntArray(); + static { + SAVED_PASSPOINT_PROVIDERS_TYPE.put(EAPConstants.EAP_SIM, NUM_EAP_SIM_TYPE); + SAVED_PASSPOINT_PROVIDERS_TYPE.put(EAPConstants.EAP_TTLS, NUM_EAP_TTLS_TYPE); + SAVED_PASSPOINT_PROVIDERS_TYPE.put(EAPConstants.EAP_TLS, NUM_EAP_TLS_TYPE); + SAVED_PASSPOINT_PROVIDERS_TYPE.put(EAPConstants.EAP_AKA, NUM_EAP_AKA_TYPE); + SAVED_PASSPOINT_PROVIDERS_TYPE.put(EAPConstants.EAP_AKA_PRIME, NUM_EAP_AKA_PRIME_TYPE); + } + private static final int NUM_PARTIAL_SCAN_RESULTS = 73; private static final int NUM_PNO_SCAN_ATTEMPTS = 20; private static final int NUM_PNO_SCAN_FAILED = 5; @@ -404,13 +426,48 @@ public class WifiMetricsTest { return testSavedNetworks; } + private PasspointProvider createMockProvider(int eapType) { + PasspointProvider provider = mock(PasspointProvider.class); + PasspointConfiguration config = new PasspointConfiguration(); + Credential credential = new Credential(); + + config.setCredential(credential); + switch (eapType) { + case EAPConstants.EAP_TLS: + credential.setCertCredential(new Credential.CertificateCredential()); + break; + case EAPConstants.EAP_TTLS: + credential.setUserCredential(new Credential.UserCredential()); + break; + case EAPConstants.EAP_AKA: + case EAPConstants.EAP_AKA_PRIME: + case EAPConstants.EAP_SIM: + Credential.SimCredential simCredential = new Credential.SimCredential(); + simCredential.setEapType(eapType); + credential.setSimCredential(simCredential); + break; + } + when(provider.getConfig()).thenReturn(config); + return provider; + } + /** * Set simple metrics, increment others */ public void setAndIncrementMetrics() throws Exception { + Map<String, PasspointProvider> providers = new HashMap<>(); mWifiMetrics.updateSavedNetworks(buildSavedNetworkList()); mWifiMetrics.updateSavedPasspointProfiles(NUM_PASSPOINT_PROVIDERS, NUM_PASSPOINT_PROVIDERS_SUCCESSFULLY_CONNECTED); + for (int i = 0; i < SAVED_PASSPOINT_PROVIDERS_TYPE.size(); i++) { + int eapType = SAVED_PASSPOINT_PROVIDERS_TYPE.keyAt(i); + int count = SAVED_PASSPOINT_PROVIDERS_TYPE.valueAt(i); + for (int j = 0; j < count; j++) { + providers.put(Integer.toString(eapType) + j, createMockProvider(eapType)); + } + } + mWifiMetrics.updateSavedPasspointProfilesInfo(providers); + mWifiMetrics.setIsLocationEnabled(TEST_VAL_IS_LOCATION_ENABLED); mWifiMetrics.setIsScanningAlwaysEnabled(IS_SCANNING_ALWAYS_ENABLED); @@ -898,6 +955,28 @@ public class WifiMetricsTest { assertEquals(NUM_CLIENT_INTERFACE_DOWN, mDecodedProto.numClientInterfaceDown); assertEquals(NUM_SOFTAP_INTERFACE_DOWN, mDecodedProto.numSoftApInterfaceDown); assertEquals(NUM_PASSPOINT_PROVIDERS, mDecodedProto.numPasspointProviders); + for (PasspointProfileTypeCount passpointProfileType : mDecodedProto + .installedPasspointProfileType) { + switch(passpointProfileType.eapMethodType) { + case PasspointProfileTypeCount.TYPE_EAP_AKA: + assertEquals(NUM_EAP_AKA_TYPE, passpointProfileType.count); + break; + case PasspointProfileTypeCount.TYPE_EAP_AKA_PRIME: + assertEquals(NUM_EAP_AKA_PRIME_TYPE, passpointProfileType.count); + break; + case PasspointProfileTypeCount.TYPE_EAP_SIM: + assertEquals(NUM_EAP_SIM_TYPE, passpointProfileType.count); + break; + case PasspointProfileTypeCount.TYPE_EAP_TLS: + assertEquals(NUM_EAP_TLS_TYPE, passpointProfileType.count); + break; + case PasspointProfileTypeCount.TYPE_EAP_TTLS: + assertEquals(NUM_EAP_TTLS_TYPE, passpointProfileType.count); + break; + default: + fail("unknown type counted"); + } + } assertEquals(NUM_PASSPOINT_PROVIDER_INSTALLATION, mDecodedProto.numPasspointProviderInstallation); assertEquals(NUM_PASSPOINT_PROVIDER_INSTALL_SUCCESS, diff --git a/tests/wifitests/src/com/android/server/wifi/WifiNativeTest.java b/tests/wifitests/src/com/android/server/wifi/WifiNativeTest.java index 31e7e553c..abec9dc8b 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(); + 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(); + assertFalse(mWifiNative.selectTxPowerScenario(sarInfo)); + verify(mWifiVendorHal).selectTxPowerScenario(sarInfo); + } } diff --git a/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java b/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java index 6a248161c..f04f53130 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java @@ -2838,4 +2838,31 @@ public class WifiServiceImplTest { mBroadcastReceiverCaptor.getValue().onReceive(mContext, intent); verifyNoMoreInteractions(mWifiCountryCode); } + + /** + * Verify calls to notify users of a softap config change check the NETWORK_SETTINGS permission. + */ + @Test + public void testNotifyUserOfApBandConversionChecksNetworkSettingsPermission() { + mWifiServiceImpl.notifyUserOfApBandConversion(TEST_PACKAGE_NAME); + verify(mContext).enforceCallingOrSelfPermission( + eq(android.Manifest.permission.NETWORK_SETTINGS), + eq("WifiService")); + verify(mWifiApConfigStore).notifyUserOfApBandConversion(eq(TEST_PACKAGE_NAME)); + } + + /** + * Verify calls to notify users do not trigger a notification when NETWORK_SETTINGS is not held + * by the caller. + */ + @Test + public void testNotifyUserOfApBandConversionThrowsExceptionWithoutNetworkSettingsPermission() { + doThrow(new SecurityException()).when(mContext) + .enforceCallingOrSelfPermission(eq(android.Manifest.permission.NETWORK_SETTINGS), + eq("WifiService")); + try { + mWifiServiceImpl.notifyUserOfApBandConversion(TEST_PACKAGE_NAME); + fail("Expected Security exception"); + } catch (SecurityException e) { } + } } diff --git a/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java b/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java index 0f8f56f66..f089ebbe7 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,547 @@ 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(); + sarInfo.isVoiceCall = 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(); + sarInfo.sarVoiceCallSupported = true; + sarInfo.sarSapSupported = false; + sarInfo.sarSensorSupported = false; + + sarInfo.isVoiceCall = 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(); + sarInfo.sarVoiceCallSupported = true; + sarInfo.sarSapSupported = false; + sarInfo.sarSensorSupported = false; + + sarInfo.isVoiceCall = 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(); + 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(); + sarInfo.sarVoiceCallSupported = true; + sarInfo.sarSapSupported = false; + sarInfo.sarSensorSupported = 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 testResetTxPowerScenario_not_needed_1_1() throws RemoteException { + InOrder inOrder = inOrder(mIWifiChipV11); + + // Create a SAR info record (no sensor or SAP support) + SarInfo sarInfo = new SarInfo(); + sarInfo.sarVoiceCallSupported = true; + sarInfo.sarSapSupported = false; + sarInfo.sarSensorSupported = 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 or SAP support) + SarInfo sarInfo = new SarInfo(); + sarInfo.sarVoiceCallSupported = true; + sarInfo.sarSapSupported = false; + sarInfo.sarSensorSupported = 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 or SAP support) + SarInfo sarInfo = new SarInfo(); + sarInfo.sarVoiceCallSupported = true; + sarInfo.sarSapSupported = false; + sarInfo.sarSensorSupported = 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 no sensor support, but with + * SAP and voice call support. + * When SAP is enabled, should result in SAP with near body scenario + * Using IWifiChip 1.2 interface + */ + @Test + public void testSapScenarios_SelectTxPowerV1_2() throws RemoteException { + // Create a SAR info record (with sensor and SAP support) + SarInfo sarInfo = new SarInfo(); + sarInfo.sarVoiceCallSupported = true; + sarInfo.sarSapSupported = true; + sarInfo.sarSensorSupported = false; + + sarInfo.isWifiSapEnabled = true; + + // Expose the 1.2 IWifiChip. + mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mLooper.getLooper()); + when(mIWifiChipV12.selectTxPowerScenario_1_2(anyInt())).thenReturn(mWifiStatusSuccess); + + // ON_BODY_CELL_ON + assertTrue(mWifiVendorHal.startVendorHalSta()); + assertTrue(mWifiVendorHal.selectTxPowerScenario(sarInfo)); + verify(mIWifiChipV12).selectTxPowerScenario_1_2( + eq(android.hardware.wifi.V1_2.IWifiChip.TxPowerScenario.ON_BODY_CELL_ON)); + verify(mIWifiChipV12, never()).resetTxPowerScenario(); + mWifiVendorHal.stopVendorHal(); + } + + /** + * Test the selectTxPowerScenario HIDL method invocation with no sensor support, but with + * SAP and voice call support. + * When a voice call is ongoing, should result in cell with near head scenario + * Using IWifiChip 1.2 interface */ @Test - public void testInvalidSelectTxPowerScenario() throws RemoteException { + public void testVoiceCallScenarios_SelectTxPowerV1_2() throws RemoteException { + // Create a SAR info record (with sensor and SAP support) + SarInfo sarInfo = new SarInfo(); + sarInfo.sarVoiceCallSupported = true; + sarInfo.sarSapSupported = true; + sarInfo.sarSensorSupported = false; + + sarInfo.isVoiceCall = true; + + // Expose the 1.2 IWifiChip. + mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mLooper.getLooper()); + when(mIWifiChipV12.selectTxPowerScenario_1_2(anyInt())).thenReturn(mWifiStatusSuccess); + + // ON_HEAD_CELL_ON + assertTrue(mWifiVendorHal.startVendorHalSta()); + assertTrue(mWifiVendorHal.selectTxPowerScenario(sarInfo)); + verify(mIWifiChipV12).selectTxPowerScenario_1_2( + eq(android.hardware.wifi.V1_2.IWifiChip.TxPowerScenario.ON_HEAD_CELL_ON)); + verify(mIWifiChipV12, never()).resetTxPowerScenario(); + 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 and SAP support) + SarInfo sarInfo = new SarInfo(); + sarInfo.sarVoiceCallSupported = true; + sarInfo.sarSapSupported = true; + sarInfo.sarSensorSupported = true; + + sarInfo.sensorState = 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 and SAP support) + SarInfo sarInfo = new SarInfo(); + sarInfo.sarVoiceCallSupported = true; + sarInfo.sarSapSupported = true; + sarInfo.sarSensorSupported = true; + + sarInfo.sensorState = 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 and SAR support) + SarInfo sarInfo = new SarInfo(); + sarInfo.sarVoiceCallSupported = true; + sarInfo.sarSapSupported = true; + sarInfo.sarSensorSupported = true; + + sarInfo.sensorState = 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.isVoiceCall = 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 and SAP support) + SarInfo sarInfo = new SarInfo(); + sarInfo.sarVoiceCallSupported = true; + sarInfo.sarSapSupported = true; + sarInfo.sarSensorSupported = true; + + sarInfo.sensorState = 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.isVoiceCall = 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 and SAP support) + SarInfo sarInfo = new SarInfo(); + sarInfo.sarVoiceCallSupported = true; + sarInfo.sarSapSupported = true; + sarInfo.sarSensorSupported = true; + + sarInfo.sensorState = 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 and SAP support) + SarInfo sarInfo = new SarInfo(); + sarInfo.sarVoiceCallSupported = true; + sarInfo.sarSapSupported = true; + sarInfo.sarSensorSupported = true; + + sarInfo.sensorState = SarInfo.SAR_SENSOR_NEAR_HEAD; + sarInfo.isWifiSapEnabled = true; + sarInfo.isVoiceCall = 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 and SAP support) + SarInfo sarInfo = new SarInfo(); + sarInfo.sarVoiceCallSupported = true; + sarInfo.sarSapSupported = true; + sarInfo.sarSensorSupported = true; + + sarInfo.sensorState = SarInfo.SAR_SENSOR_NEAR_HEAD; + sarInfo.isWifiSapEnabled = true; + sarInfo.isVoiceCall = 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 and SAP support) + SarInfo sarInfo = new SarInfo(); + sarInfo.sarVoiceCallSupported = true; + sarInfo.sarSapSupported = true; + sarInfo.sarSensorSupported = true; + + sarInfo.sensorState = SarInfo.SAR_SENSOR_FREE_SPACE; + sarInfo.isWifiSapEnabled = true; + sarInfo.isVoiceCall = 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(); + sarInfo.sarVoiceCallSupported = true; + sarInfo.sarSapSupported = true; + sarInfo.sarSensorSupported = true; + + sarInfo.isWifiSapEnabled = true; + sarInfo.isVoiceCall = 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(); } diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java index 2a76d6a32..abcd0bf34 100644 --- a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java +++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java @@ -284,6 +284,7 @@ public class PasspointManagerTest { PasspointProvider provider = createMockProvider(config); when(mObjectFactory.makePasspointProvider(eq(config), eq(mWifiKeyStore), eq(mSimAccessor), anyLong(), eq(TEST_CREATOR_UID))).thenReturn(provider); + assertTrue(mManager.addOrUpdateProvider(config, TEST_CREATOR_UID)); return provider; @@ -1378,9 +1379,10 @@ public class PasspointManagerTest { * @throws Exception */ @Test - public void updateMetrics() throws Exception { + public void updateMetrics() { PasspointProvider provider = addTestProvider(TEST_FQDN); - + ArgumentCaptor<Map<String, PasspointProvider>> argCaptor = ArgumentCaptor.forClass( + Map.class); // Provider have not provided a successful network connection. int expectedInstalledProviders = 1; int expectedConnectedProviders = 0; @@ -1388,7 +1390,10 @@ public class PasspointManagerTest { mManager.updateMetrics(); verify(mWifiMetrics).updateSavedPasspointProfiles( eq(expectedInstalledProviders), eq(expectedConnectedProviders)); - reset(provider); + + verify(mWifiMetrics).updateSavedPasspointProfilesInfo(argCaptor.capture()); + assertEquals(expectedInstalledProviders, argCaptor.getValue().size()); + assertEquals(provider, argCaptor.getValue().get(TEST_FQDN)); reset(mWifiMetrics); // Provider have provided a successful network connection. @@ -1398,6 +1403,7 @@ public class PasspointManagerTest { verify(mWifiMetrics).updateSavedPasspointProfiles( eq(expectedInstalledProviders), eq(expectedConnectedProviders)); } + /** * Verify Passpoint Manager's provisioning APIs by invoking methods in PasspointProvisioner for * initiailization and provisioning a provider. diff --git a/tests/wifitests/src/com/android/server/wifi/rtt/RttNativeTest.java b/tests/wifitests/src/com/android/server/wifi/rtt/RttNativeTest.java index 3f5aa5f2d..7bbe6bb79 100644 --- a/tests/wifitests/src/com/android/server/wifi/rtt/RttNativeTest.java +++ b/tests/wifitests/src/com/android/server/wifi/rtt/RttNativeTest.java @@ -23,6 +23,7 @@ import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; @@ -50,10 +51,13 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ErrorCollector; import org.mockito.ArgumentCaptor; +import org.mockito.InOrder; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** @@ -62,6 +66,7 @@ import java.util.List; public class RttNativeTest { private RttNative mDut; private WifiStatus mStatusSuccess; + private WifiStatus mStatusRttControllerInvalid; private ArgumentCaptor<ArrayList> mRttConfigCaptor = ArgumentCaptor.forClass(ArrayList.class); private ArgumentCaptor<List> mRttResultCaptor = ArgumentCaptor.forClass(List.class); @@ -97,6 +102,9 @@ public class RttNativeTest { when(mockRttController.rangeCancel(anyInt(), any(ArrayList.class))).thenReturn( mStatusSuccess); + mStatusRttControllerInvalid = new WifiStatus(); + mStatusRttControllerInvalid.code = WifiStatusCode.ERROR_WIFI_RTT_CONTROLLER_INVALID; + mDut = new RttNative(mockRttServiceImpl, mockHalDeviceManager); mDut.start(null); verify(mockHalDeviceManager).registerStatusListener(mHdmStatusListener.capture(), any()); @@ -324,15 +332,18 @@ public class RttNativeTest { int cmdId = 55; RangingRequest request = RttTestUtils.getDummyRangingRequest((byte) 0); + InOrder order = inOrder(mockRttServiceImpl); + // (1) configure Wi-Fi down and send a status change indication when(mockHalDeviceManager.isStarted()).thenReturn(false); mHdmStatusListener.getValue().onStatusChanged(); - verify(mockRttServiceImpl).disable(); + order.verify(mockRttServiceImpl).disable(); assertFalse(mDut.isReady()); // (2) issue range request mDut.rangeRequest(cmdId, request, true); + order.verify(mockRttServiceImpl).disable(); // due to re-init attempt verifyNoMoreInteractions(mockRttServiceImpl, mockRttController); } @@ -436,6 +447,77 @@ public class RttNativeTest { } } + /** + * Validate correct re-initialization when the RTT Controller is invalid (which can happen + * due to mode change in the HAL). Expect a re-initialization and a rerun of the range request. + */ + @Test + public void testRangeRequestInvalidRttControllerOnce() throws Exception { + int cmdId = 55; + RangingRequest request = RttTestUtils.getDummyRangingRequest((byte) 0); + + InOrder inOrder = Mockito.inOrder(mockRttController); + + // configure failure then success + when(mockRttController.rangeRequest(anyInt(), any(ArrayList.class))).thenReturn( + mStatusRttControllerInvalid).thenReturn(mStatusSuccess); + + // issue range request + boolean result = mDut.rangeRequest(cmdId, request, true); + assertTrue("rangeRequest should succeeed", result); + + // verify HAL call + inOrder.verify(mockRttController).rangeRequest(eq(cmdId), mRttConfigCaptor.capture()); + ArrayList<RttConfig> halRequest1 = mRttConfigCaptor.getValue(); + + // verify re-initialization (i.e. callback re-registered) + inOrder.verify(mockRttController).registerEventCallback(any()); + + // verify HAL call + inOrder.verify(mockRttController).rangeRequest(eq(cmdId), mRttConfigCaptor.capture()); + ArrayList<RttConfig> halRequest2 = mRttConfigCaptor.getValue(); + + assertTrue("HAL parameters different between calls!", + Arrays.equals(halRequest1.toArray(), halRequest2.toArray())); + verifyNoMoreInteractions(mockRttController); + } + + /** + * Validate correct re-initialization when the RTT Controller is invalid (which can happen + * due to mode change in the HAL). Expect a re-initialization and a rerun of the range request - + * but only once (no infinite loop). + */ + @Test + public void testRangeRequestInvalidRttControllerForever() throws Exception { + int cmdId = 55; + RangingRequest request = RttTestUtils.getDummyRangingRequest((byte) 0); + + InOrder inOrder = Mockito.inOrder(mockRttController); + + // configure failure + when(mockRttController.rangeRequest(anyInt(), any(ArrayList.class))).thenReturn( + mStatusRttControllerInvalid); + + // issue range request + boolean result = mDut.rangeRequest(cmdId, request, true); + assertFalse("rangeRequest should fail", result); + + // verify HAL call + inOrder.verify(mockRttController).rangeRequest(eq(cmdId), mRttConfigCaptor.capture()); + ArrayList<RttConfig> halRequest1 = mRttConfigCaptor.getValue(); + + // verify re-initialization (i.e. callback re-registered) + inOrder.verify(mockRttController).registerEventCallback(any()); + + // verify HAL call + inOrder.verify(mockRttController).rangeRequest(eq(cmdId), mRttConfigCaptor.capture()); + ArrayList<RttConfig> halRequest2 = mRttConfigCaptor.getValue(); + + assertTrue("HAL parameters different between calls!", + Arrays.equals(halRequest1.toArray(), halRequest2.toArray())); + verifyNoMoreInteractions(mockRttController); + } + // Utilities /** |