diff options
3 files changed, 656 insertions, 138 deletions
diff --git a/service/java/com/android/server/wifi/SarManager.java b/service/java/com/android/server/wifi/SarManager.java index da48a8537..f157ac41e 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; @@ -41,23 +43,25 @@ import java.util.List; * It deals with the following: * - Tracking the STA state through calls from the ClientModeManager. * - Tracking the state of the Cellular calls or data. - * - Based on above, selecting the SAR profile to use and programming it in wifi hal. + * - Tracking the sensor indicating proximity to user head/hand/body. + * - It constructs the sar info and send it towards the HAL */ public class SarManager { - /* For Logging */ private static final String TAG = "WifiSarManager"; private boolean mVerboseLoggingEnabled = true; + private SarInfo mSarInfo; + /* Configuration for SAR */ private boolean mEnableSarTxPowerLimit; + private boolean mEnableSarBodyProximity; + /* Sensor event definitions */ + private int mSarSensorEventFreeSpace; + private int mSarSensorEventNearBody; + private int mSarSensorEventNearHand; + private int mSarSensorEventNearHead; - /* Current SAR Scenario */ - private int mCurrentSarScenario = WifiNative.TX_POWER_SCENARIO_NORMAL; - - /* Booleans for Cell and wifi states */ - private boolean mCellOn = false; - private boolean mWifiStaEnabled = false; /** * Other parameters passed in or created in the constructor. */ @@ -65,6 +69,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 +79,105 @@ public class SarManager { SarManager(Context context, TelephonyManager telephonyManager, Looper looper, - WifiNative wifiNative) { + WifiNative wifiNative, + SensorManager sensorManager) { mContext = context; mTelephonyManager = telephonyManager; mWifiNative = wifiNative; mLooper = looper; + mSensorManager = sensorManager; mPhoneStateListener = new WifiPhoneStateListener(looper); + mSensorListener = new SarSensorEventListener(); + + readSarConfigs(); + if (mEnableSarTxPowerLimit) { + mSarInfo = new SarInfo(mEnableSarBodyProximity); + registerListeners(); + } + } + + private void readSarConfigs() { + mEnableSarTxPowerLimit = mContext.getResources().getBoolean( + R.bool.config_wifi_framework_enable_sar_tx_power_limit); + /* In case SAR is disabled, + then SAR sensor is automatically disabled as well (irrespective of the config) */ + if (!mEnableSarTxPowerLimit) { + mEnableSarBodyProximity = false; + return; + } - registerListeners(); + mEnableSarBodyProximity = mContext.getResources().getBoolean( + R.bool.config_wifi_framework_enable_body_proximity_sar_tx_power_limit); + + /* Read the sar sensor event Ids */ + if (mEnableSarBodyProximity) { + mSarSensorEventFreeSpace = mContext.getResources().getInteger( + R.integer.config_wifi_framework_sar_free_space_event_id); + mSarSensorEventNearBody = mContext.getResources().getInteger( + R.integer.config_wifi_framework_sar_near_body_event_id); + mSarSensorEventNearHand = mContext.getResources().getInteger( + R.integer.config_wifi_framework_sar_near_hand_event_id); + mSarSensorEventNearHead = mContext.getResources().getInteger( + R.integer.config_wifi_framework_sar_near_head_event_id); + } + } + + private void registerListeners() { + /* Listen for Phone State changes */ + registerPhoneStateListener(); + + /* Only listen for SAR sensor if supported */ + if (mEnableSarBodyProximity) { + /* Register the SAR sensor listener. + * If this fails, we will assume worst case (near head) */ + if (!registerSensorListener()) { + Log.e(TAG, "Failed to register sensor listener, setting Sensor to NearHead"); + /*TODO Need to add a metric to determine how often this happens */ + mSarInfo.mSensorState = SarInfo.SAR_SENSOR_NEAR_HEAD; + } + } } /** - * Starts the SAR Manager by initializing the different listeners + * Register the phone state listener. */ - private void registerListeners() { - /* First read the configuration for SAR Support */ - mEnableSarTxPowerLimit = mContext.getResources().getBoolean( - R.bool.config_wifi_framework_enable_voice_call_sar_tx_power_limit); + private void registerPhoneStateListener() { + Log.i(TAG, "Registering for telephony call state changes"); + mTelephonyManager.listen( + mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE); + } - /* Only Start listening for events if SAR is enabled */ - if (mEnableSarTxPowerLimit) { - Log.d(TAG, "Registering Listeners for the SAR Manager"); + /** + * Register the body/hand/head proximity sensor. + */ + private boolean registerSensorListener() { + Log.i(TAG, "Registering for Sensor notification Listener"); + return mSensorListener.register(); + } + + /** + * Update Wifi Client State + */ + public void setClientWifiState(int state) { + boolean newIsEnabled; + /* No action is taken if SAR is not enabled */ + if (!mEnableSarTxPowerLimit) { + return; + } + + if (state == WifiManager.WIFI_STATE_DISABLED) { + newIsEnabled = false; + } else if (state == WifiManager.WIFI_STATE_ENABLED) { + newIsEnabled = true; + } else { + /* No change so exiting with no action */ + return; + } - /* Listen for Phone State changes */ - registerPhoneListener(); + /* Report change to HAL if needed */ + if (mSarInfo.mIsWifiClientEnabled != newIsEnabled) { + mSarInfo.mIsWifiClientEnabled = newIsEnabled; + updateSarScenario(); } } @@ -104,42 +185,52 @@ public class SarManager { * Report Cell state event */ private void onCellStateChangeEvent(int state) { - boolean currentCellOn = mCellOn; - + boolean newIsVoiceCall; switch (state) { case CALL_STATE_OFFHOOK: case CALL_STATE_RINGING: - mCellOn = true; + newIsVoiceCall = true; break; case CALL_STATE_IDLE: - mCellOn = false; + newIsVoiceCall = false; break; default: Log.e(TAG, "Invalid Cell State: " + state); + return; } - if (mCellOn != currentCellOn) { + /* Report change to HAL if needed */ + if (mSarInfo.mIsVoiceCall != newIsVoiceCall) { + mSarInfo.mIsVoiceCall = newIsVoiceCall; updateSarScenario(); } } /** - * Update Wifi Client State + * Report an event from the SAR sensor */ - public void setClientWifiState(int state) { - /* No action is taken if SAR is not enabled */ - if (!mEnableSarTxPowerLimit) return; - - if (state == WifiManager.WIFI_STATE_DISABLED && mWifiStaEnabled) { - mWifiStaEnabled = false; - } else if (state == WifiManager.WIFI_STATE_ENABLED && !mWifiStaEnabled) { - mWifiStaEnabled = true; + private void onSarSensorEvent(int sarSensorEvent) { + int newSensorState; + if (sarSensorEvent == mSarSensorEventFreeSpace) { + newSensorState = SarInfo.SAR_SENSOR_FREE_SPACE; + } else if (sarSensorEvent == mSarSensorEventNearBody) { + newSensorState = SarInfo.SAR_SENSOR_NEAR_BODY; + } else if (sarSensorEvent == mSarSensorEventNearHand) { + newSensorState = SarInfo.SAR_SENSOR_NEAR_HAND; + } else if (sarSensorEvent == mSarSensorEventNearHead) { + newSensorState = SarInfo.SAR_SENSOR_NEAR_HEAD; + } else { + Log.e(TAG, "Invalid SAR sensor event id: " + sarSensorEvent); + return; + } - /* Since no wifi interface was up, - time for SAR scenario to take effect */ - sendTxPowerScenario(mCurrentSarScenario); + /* Report change to HAL if needed */ + if (mSarInfo.mSensorState != newSensorState) { + Log.d(TAG, "Setting Sensor state to " + SarInfo.sensorStateToString(newSensorState)); + mSarInfo.mSensorState = newSensorState; + updateSarScenario(); } } @@ -155,18 +246,16 @@ public class SarManager { } } - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println("*** WiFi SAR Manager Dump ***"); - pw.println("Current SAR Scenario is " + scenarioToString(mCurrentSarScenario)); - } - /** - * Register the phone listener. + * dump() + * Dumps SarManager state (as well as its SarInfo member variable state) */ - private void registerPhoneListener() { - Log.i(TAG, "Registering for telephony call state changes"); - mTelephonyManager.listen( - mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE); + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println("*** WiFi SAR Manager Dump ***"); + pw.println("isSarEnabled: " + mEnableSarTxPowerLimit); + pw.println("isSarSensorEnabled: " + mEnableSarBodyProximity); + pw.println(""); + mSarInfo.dump(fd, pw, args); } /** @@ -177,69 +266,96 @@ public class SarManager { super(looper); } + /** + * onCallStateChanged() + * This callback is called when a SAR sensor event is received + * Note that this runs in the WifiStateMachineHandlerThread + * since the corresponding Looper was passed to the WifiPhoneStateListener constructor. + */ @Override public void onCallStateChanged(int state, String incomingNumber) { Log.d(TAG, "Received Phone State Change: " + state); /* In case of an unsolicited event */ - if (!mEnableSarTxPowerLimit) return; - + if (!mEnableSarTxPowerLimit) { + return; + } onCellStateChangeEvent(state); } } - /** - * update the Current SAR Scenario based on factors including: - * - Do we have an ongoing cellular voice call. - */ - private void updateSarScenario() { - int newSarScenario; + private class SarSensorEventListener implements SensorEventListener { - if (mCellOn) { - newSarScenario = WifiNative.TX_POWER_SCENARIO_VOICE_CALL; - } else { - newSarScenario = WifiNative.TX_POWER_SCENARIO_NORMAL; - } + private Sensor mSensor; + + /** + * 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/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java index 0e30af841..5a4fe570b 100644 --- a/service/java/com/android/server/wifi/WifiInjector.java +++ b/service/java/com/android/server/wifi/WifiInjector.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.app.ActivityManager; import android.app.AppOpsManager; import android.content.Context; +import android.hardware.SystemSensorManager; import android.net.NetworkKey; import android.net.NetworkScoreManager; import android.net.wifi.IWifiScanner; @@ -253,7 +254,7 @@ public class WifiInjector { this, mWifiConfigManager, mWifiPermissionsUtil, mWifiMetrics, mClock); mSarManager = new SarManager(mContext, makeTelephonyManager(), wifiStateMachineLooper, - mWifiNative); + mWifiNative, new SystemSensorManager(mContext, wifiStateMachineLooper)); if (mUseRealLogger) { mWifiDiagnostics = new WifiDiagnostics( mContext, this, mWifiNative, mBuildProperties, diff --git a/tests/wifitests/src/com/android/server/wifi/SarManagerTest.java b/tests/wifitests/src/com/android/server/wifi/SarManagerTest.java index 163280a39..69878cdcf 100644 --- a/tests/wifitests/src/com/android/server/wifi/SarManagerTest.java +++ b/tests/wifitests/src/com/android/server/wifi/SarManagerTest.java @@ -16,39 +16,44 @@ package com.android.server.wifi; +import static android.telephony.TelephonyManager.CALL_STATE_IDLE; +import static android.telephony.TelephonyManager.CALL_STATE_OFFHOOK; + import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.Mockito.*; -import static android.telephony.TelephonyManager.CALL_STATE_IDLE; -import static android.telephony.TelephonyManager.CALL_STATE_OFFHOOK; - -import android.app.test.MockAnswerUtil.AnswerWithArguments; -import android.content.BroadcastReceiver; import android.content.Context; -import android.content.IntentFilter; import android.content.pm.ApplicationInfo; +import android.hardware.Sensor; import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SystemSensorManager; import android.net.wifi.WifiManager; import android.os.Build; import android.os.test.TestLooper; import android.support.test.filters.SmallTest; import android.telephony.PhoneStateListener; import android.telephony.TelephonyManager; -import android.util.Log; import com.android.internal.R; import org.junit.After; import org.junit.Before; import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; import org.mockito.ArgumentCaptor; import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + /** * unit tests for {@link com.android.server.wifi.SarManager}. */ @@ -56,6 +61,12 @@ import org.mockito.MockitoAnnotations; public class SarManagerTest { private static final String TAG = "WifiSarManagerTest"; private static final String OP_PACKAGE_NAME = "com.xxx"; + private static final String SAR_SENSOR_NAME = "com.google.sensor.sar"; + + private static final int SAR_SENSOR_EVENT_FREE_SPACE = 1; + private static final int SAR_SENSOR_EVENT_HAND = 2; + private static final int SAR_SENSOR_EVENT_HEAD = 3; + private static final int SAR_SENSOR_EVENT_BODY = 4; private void enableDebugLogs() { mSarMgr.enableVerboseLogging(1); @@ -70,23 +81,26 @@ public class SarManagerTest { private TestLooper mLooper; private MockResources mResources; private PhoneStateListener mPhoneStateListener; + private List<Sensor> mSensorList; + private Sensor mSensor; + private SarInfo mSarInfo; - @Mock private Context mContext; + @Mock private Context mContext; + @Mock SensorEventListener mSensorEventListener; + @Mock SystemSensorManager mSensorManager; @Mock TelephonyManager mTelephonyManager; @Mock private ApplicationInfo mMockApplInfo; @Mock WifiNative mWifiNative; @Before public void setUp() throws Exception { - Log.e(TAG, "Setting Up ..."); - - // Ensure Looper exists + /* Ensure Looper exists */ mLooper = new TestLooper(); MockitoAnnotations.initMocks(this); /* Default behavior is to return with success */ - when(mWifiNative.selectTxPowerScenario(anyInt())).thenReturn(true); + when(mWifiNative.selectTxPowerScenario(any(SarInfo.class))).thenReturn(true); mResources = getMockResources(); @@ -105,15 +119,78 @@ public class SarManagerTest { } /** + * Helper function to capture SarInfo object + */ + private void captureSarInfo(WifiNative wifiNative) { + /* Capture the SensorEventListener */ + ArgumentCaptor<SarInfo> sarInfoCaptor = ArgumentCaptor.forClass(SarInfo.class); + verify(wifiNative).selectTxPowerScenario(sarInfoCaptor.capture()); + mSarInfo = sarInfoCaptor.getValue(); + assertNotNull(mSarInfo); + } + + /** + * Helper function to create and prepare sensor info + */ + private void prepareSensorInfo(boolean registerReturn) { + /* Create a sensor object (note, this can not be mocked since it is a final class) */ + Constructor<Sensor> constructor = + (Constructor<Sensor>) Sensor.class.getDeclaredConstructors()[0]; + constructor.setAccessible(true); + + try { + mSensor = constructor.newInstance(); + } catch (Exception e) { + fail("Failed to create a sensor object"); + } + + /* Now set the mStringType field with the proper field */ + Field declaredField = null; + try { + declaredField = Sensor.class.getDeclaredField("mStringType"); + declaredField.setAccessible(true); + declaredField.set(mSensor, SAR_SENSOR_NAME); + } catch (Exception e) { + fail("Could not set sensor string type"); + } + + /* Prepare the sensor list */ + mSensorList = new ArrayList<Sensor>(); + mSensorList.add(mSensor); + when(mSensorManager.getSensorList(Sensor.TYPE_ALL)).thenReturn(mSensorList); + when(mSensorManager.registerListener(any(SensorEventListener.class), any(Sensor.class), + anyInt())).thenReturn(registerReturn); + } + + /** * Helper function to set configuration for SAR and create the SAR Manager * */ - private void createSarManager(boolean isSarEnabled) { + private void createSarManager(boolean isSarEnabled, boolean isSarSensorEnabled) { mResources.setBoolean( - R.bool.config_wifi_framework_enable_voice_call_sar_tx_power_limit, isSarEnabled); + R.bool.config_wifi_framework_enable_sar_tx_power_limit, isSarEnabled); + mResources.setBoolean( + 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 +199,88 @@ 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_body_proximity_sar_tx_power_limit, true); + if (addToConfigs) { + mResources.setString(R.string.config_wifi_sar_sensor_type, configSensorName); } + /* Set the event id configs */ + mResources.setInteger(R.integer.config_wifi_framework_sar_free_space_event_id, + SAR_SENSOR_EVENT_FREE_SPACE); + mResources.setInteger(R.integer.config_wifi_framework_sar_near_hand_event_id, + SAR_SENSOR_EVENT_HAND); + mResources.setInteger(R.integer.config_wifi_framework_sar_near_head_event_id, + SAR_SENSOR_EVENT_HEAD); + mResources.setInteger(R.integer.config_wifi_framework_sar_near_body_event_id, + SAR_SENSOR_EVENT_BODY); + + prepareSensorInfo(sensorRegisterReturn); + + mSarMgr = new SarManager(mContext, mTelephonyManager, mLooper.getLooper(), + mWifiNative, mSensorManager); + + /* Capture the PhoneStateListener */ + ArgumentCaptor<PhoneStateListener> phoneStateListenerCaptor = + ArgumentCaptor.forClass(PhoneStateListener.class); + verify(mTelephonyManager).listen(phoneStateListenerCaptor.capture(), + eq(PhoneStateListener.LISTEN_CALL_STATE)); + mPhoneStateListener = phoneStateListenerCaptor.getValue(); + assertNotNull(mPhoneStateListener); + /* Enable logs from SarManager */ enableDebugLogs(); } /** + * Helper function to create and pass a sensor event + */ + private void sendSensorEvent(int eventId) { + SensorEvent event; + Constructor<SensorEvent> constructor = + (Constructor<SensorEvent>) SensorEvent.class.getDeclaredConstructors()[0]; + constructor.setAccessible(true); + + try { + event = constructor.newInstance(1); + event.values[0] = (float) eventId; + mSensorEventListener.onSensorChanged(event); + } catch (Exception e) { + fail("Failed to create a Sensor Event"); + } + } + + /** * Test that we do register the telephony call state listener on devices which do support * setting/resetting Tx power limit. */ @Test public void testSarMgr_enabledTxPowerScenario_registerPhone() throws Exception { - createSarManager(true); + createSarManager(true, false); verify(mTelephonyManager).listen(any(), eq(PhoneStateListener.LISTEN_CALL_STATE)); } @@ -144,7 +290,7 @@ public class SarManagerTest { */ @Test public void testSarMgr_disabledTxPowerScenario_registerPhone() throws Exception { - createSarManager(false); + createSarManager(false, false); verify(mTelephonyManager, never()).listen(any(), anyInt()); } @@ -153,27 +299,24 @@ public class SarManagerTest { * Tx power scenario upon receiving {@link TelephonyManager#CALL_STATE_OFFHOOK} when WiFi STA * is enabled * In this case Wifi is enabled first, then off-hook is detected - * Expectation is to get {@link WifiNative#TX_POWER_SCENARIO_NORMAL} when WiFi is turned on - * followed by {@link WifiNative#TX_POWER_SCENARIO_VOICE_CALL} when OFFHOOK event is detected */ @Test public void testSarMgr_enabledTxPowerScenario_wifiOn_offHook() throws Exception { - createSarManager(true); - assertNotNull(mPhoneStateListener); + createSarManager(true, false); InOrder inOrder = inOrder(mWifiNative); /* Enable WiFi State */ mSarMgr.setClientWifiState(WifiManager.WIFI_STATE_ENABLED); + captureSarInfo(mWifiNative); - inOrder.verify(mWifiNative).selectTxPowerScenario( - eq(WifiNative.TX_POWER_SCENARIO_NORMAL)); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertFalse(mSarInfo.mIsVoiceCall); /* Set phone state to OFFHOOK */ mPhoneStateListener.onCallStateChanged(CALL_STATE_OFFHOOK, ""); - - inOrder.verify(mWifiNative).selectTxPowerScenario( - eq(WifiNative.TX_POWER_SCENARIO_VOICE_CALL)); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertTrue(mSarInfo.mIsVoiceCall); } /** @@ -181,66 +324,66 @@ public class SarManagerTest { * Tx power scenario upon receiving {@link TelephonyManager#CALL_STATE_OFFHOOK} when WiFi STA * is enabled * In this case off-hook event is detected first, then wifi is turned on - * Expectation is to get {@link WifiNative#TX_POWER_SCENARIO_VOICE_CALL} once wifi is turned on */ @Test public void testSarMgr_enabledTxPowerScenario_offHook_wifiOn() throws Exception { - createSarManager(true); - assertNotNull(mPhoneStateListener); + createSarManager(true, false); + + InOrder inOrder = inOrder(mWifiNative); /* Set phone state to OFFHOOK */ mPhoneStateListener.onCallStateChanged(CALL_STATE_OFFHOOK, ""); /* Enable WiFi State */ mSarMgr.setClientWifiState(WifiManager.WIFI_STATE_ENABLED); + captureSarInfo(mWifiNative); - verify(mWifiNative).selectTxPowerScenario( - eq(WifiNative.TX_POWER_SCENARIO_VOICE_CALL)); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertTrue(mSarInfo.mIsVoiceCall); } /** * Test that for devices that support setting/resetting Tx Power limits, device sets the proper * Tx power scenarios upon receiving {@link TelephonyManager#CALL_STATE_OFFHOOK} and - * {@link TelephonyManager#CALL_STATE_OFFHOOK} when WiFi STA is enabled + * {@link TelephonyManager#CALL_STATE_IDLE} when WiFi STA is enabled */ @Test public void testSarMgr_enabledTxPowerScenario_wifiOn_offHook_onHook() throws Exception { - createSarManager(true); - assertNotNull(mPhoneStateListener); + createSarManager(true, false); InOrder inOrder = inOrder(mWifiNative); /* Enable WiFi State */ mSarMgr.setClientWifiState(WifiManager.WIFI_STATE_ENABLED); + captureSarInfo(mWifiNative); /* Now device should set tx power scenario to NORMAL */ - inOrder.verify(mWifiNative).selectTxPowerScenario( - eq(WifiNative.TX_POWER_SCENARIO_NORMAL)); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertFalse(mSarInfo.mIsVoiceCall); /* Set phone state to OFFHOOK */ mPhoneStateListener.onCallStateChanged(CALL_STATE_OFFHOOK, ""); /* Device should set tx power scenario to Voice call */ - inOrder.verify(mWifiNative).selectTxPowerScenario( - eq(WifiNative.TX_POWER_SCENARIO_VOICE_CALL)); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertTrue(mSarInfo.mIsVoiceCall); /* Set state back to ONHOOK */ mPhoneStateListener.onCallStateChanged(CALL_STATE_IDLE, ""); /* Device should set tx power scenario to NORMAL again */ - inOrder.verify(mWifiNative).selectTxPowerScenario( - eq(WifiNative.TX_POWER_SCENARIO_NORMAL)); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertFalse(mSarInfo.mIsVoiceCall); } /** * Test that for devices that support setting/resetting Tx Power limits, device does not * sets the Tx power scenarios upon receiving {@link TelephonyManager#CALL_STATE_OFFHOOK} and - * {@link TelephonyManager#CALL_STATE_OFFHOOK} when WiFi STA is disabled + * {@link TelephonyManager#CALL_STATE_IDLE} when WiFi STA is disabled */ @Test public void testSarMgr_enabledTxPowerScenario_wifiOff_offHook_onHook() throws Exception { - createSarManager(true); - assertNotNull(mPhoneStateListener); + createSarManager(true, false); InOrder inOrder = inOrder(mWifiNative); @@ -251,6 +394,264 @@ 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 a device that has SAR enabled, with sar sensor enabled, + * wifi enabled, Then Tx power scenarios follow events from sensor for body/hand/head/none + */ + @Test + public void testSarMgr_sarSensorOn_WifiOn_sensorEventsTriggered() throws Exception { + createSarManager(true, true); + + InOrder inOrder = inOrder(mWifiNative); + + /* Enable Wifi Client */ + mSarMgr.setClientWifiState(WifiManager.WIFI_STATE_ENABLED); + captureSarInfo(mWifiNative); + + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_FREE_SPACE, mSarInfo.mSensorState); + + /* Sensor event */ + sendSensorEvent(SAR_SENSOR_EVENT_BODY); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_BODY, mSarInfo.mSensorState); + assertFalse(mSarInfo.mIsVoiceCall); + + /* Sensor event */ + sendSensorEvent(SAR_SENSOR_EVENT_HEAD); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HEAD, mSarInfo.mSensorState); + assertFalse(mSarInfo.mIsVoiceCall); + + /* Sensor event */ + sendSensorEvent(SAR_SENSOR_EVENT_HAND); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HAND, mSarInfo.mSensorState); + assertFalse(mSarInfo.mIsVoiceCall); + + /* Sensor event */ + sendSensorEvent(SAR_SENSOR_EVENT_FREE_SPACE); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_FREE_SPACE, mSarInfo.mSensorState); + assertFalse(mSarInfo.mIsVoiceCall); + } + + /** + * Test that for a device that has SAR enabled, with sar sensor enabled, + * wifi enabled, cellOn, + * then Tx power scenarios follow events from sensor for body/hand/head/none + */ + @Test + public void testSarMgr_sarSensorOn_wifiOn_cellOn_sensorEventsTriggered() throws Exception { + createSarManager(true, true); + + InOrder inOrder = inOrder(mWifiNative); + + /* Enable Wifi Client */ + mSarMgr.setClientWifiState(WifiManager.WIFI_STATE_ENABLED); + captureSarInfo(mWifiNative); + + /* Should get the an event with no calls */ + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_FREE_SPACE, mSarInfo.mSensorState); + assertFalse(mSarInfo.mIsVoiceCall); + + /* Start a Cell call */ + mPhoneStateListener.onCallStateChanged(CALL_STATE_OFFHOOK, ""); + inOrder.verify(mWifiNative).selectTxPowerScenario(any(SarInfo.class)); + assertEquals(SarInfo.SAR_SENSOR_FREE_SPACE, mSarInfo.mSensorState); + assertTrue(mSarInfo.mIsVoiceCall); + + /* Sensor event */ + sendSensorEvent(SAR_SENSOR_EVENT_BODY); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_BODY, mSarInfo.mSensorState); + assertTrue(mSarInfo.mIsVoiceCall); + + /* Sensor event */ + sendSensorEvent(SAR_SENSOR_EVENT_HEAD); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HEAD, mSarInfo.mSensorState); + assertTrue(mSarInfo.mIsVoiceCall); + + /* Sensor event */ + sendSensorEvent(SAR_SENSOR_EVENT_HAND); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HAND, mSarInfo.mSensorState); + assertTrue(mSarInfo.mIsVoiceCall); + + /* Sensor event */ + sendSensorEvent(SAR_SENSOR_EVENT_FREE_SPACE); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_FREE_SPACE, mSarInfo.mSensorState); + assertTrue(mSarInfo.mIsVoiceCall); + } + + /** + * Test that for a device that has SAR enabled, with sar sensor enabled, + * wifi enabled, device next to user head, a call has started and stopped, + * then Tx power scenarios should adjust properly + */ + @Test + public void testSarMgr_sarSensorOn_wifiOn_onHead_cellOnOff() throws Exception { + createSarManager(true, true); + + InOrder inOrder = inOrder(mWifiNative); + + /* Enable Wifi Client */ + mSarMgr.setClientWifiState(WifiManager.WIFI_STATE_ENABLED); + captureSarInfo(mWifiNative); + + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_FREE_SPACE, mSarInfo.mSensorState); + assertFalse(mSarInfo.mIsVoiceCall); + + /* Sensor event */ + sendSensorEvent(SAR_SENSOR_EVENT_HEAD); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HEAD, mSarInfo.mSensorState); + assertFalse(mSarInfo.mIsVoiceCall); + + /* Start a Cell call */ + mPhoneStateListener.onCallStateChanged(CALL_STATE_OFFHOOK, ""); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HEAD, mSarInfo.mSensorState); + assertTrue(mSarInfo.mIsVoiceCall); + + /* End a Cell call */ + mPhoneStateListener.onCallStateChanged(CALL_STATE_IDLE, ""); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HEAD, mSarInfo.mSensorState); + assertFalse(mSarInfo.mIsVoiceCall); + } + + /** + * Test that for a device that has SAR enabled, with sar sensor enabled, + * all wifi states disabled, when a sensor event is triggered no setting of Tx power scenario + * is initiated. + * Then when Wifi is enabled, Tx power setting will be initiated to reflect the sensor event. + */ + @Test + public void testSarMgr_sarSensorOn_WifiOffOn_sensorEventTriggered() throws Exception { + createSarManager(true, true); + + InOrder inOrder = inOrder(mWifiNative); + + /* Sensor event */ + sendSensorEvent(SAR_SENSOR_EVENT_BODY); + inOrder.verify(mWifiNative, never()).selectTxPowerScenario(any(SarInfo.class)); + + /* Enable Wifi Client */ + mSarMgr.setClientWifiState(WifiManager.WIFI_STATE_ENABLED); + captureSarInfo(mWifiNative); + + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_BODY, mSarInfo.mSensorState); + assertFalse(mSarInfo.mIsVoiceCall); + } + + /** + * Test the error case when SAR sensor name does not exist in configuration. + * In this case, SarManager should assume operation near head all the time. + */ + @Test + public void testSarMgr_error_sar_name_does_not_exist() throws Exception { + createSarManagerSensorNegTest(SAR_SENSOR_NAME, false, true); + + InOrder inOrder = inOrder(mWifiNative); + + verify(mSensorManager, never()).registerListener(any(SensorEventListener.class), + any(Sensor.class), anyInt()); + + /* Enable WiFi Client */ + mSarMgr.setClientWifiState(WifiManager.WIFI_STATE_ENABLED); + captureSarInfo(mWifiNative); + + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HEAD, mSarInfo.mSensorState); + assertFalse(mSarInfo.mIsVoiceCall); + + /* Start a Cell Call */ + mPhoneStateListener.onCallStateChanged(CALL_STATE_OFFHOOK, ""); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HEAD, mSarInfo.mSensorState); + assertTrue(mSarInfo.mIsVoiceCall); + + /* End the call */ + mPhoneStateListener.onCallStateChanged(CALL_STATE_IDLE, ""); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HEAD, mSarInfo.mSensorState); + assertFalse(mSarInfo.mIsVoiceCall); + } + + /** + * Test the error case when SarManager uses the wrong sensor name in configuration. + * In this case, SarManager should assume operation near head all the time. + */ + @Test + public void testSarMgr_error_sar_name_mismatch() throws Exception { + createSarManagerSensorNegTest("wrong.sensor.name", true, true); + + InOrder inOrder = inOrder(mWifiNative); + + verify(mSensorManager, never()).registerListener(any(SensorEventListener.class), + any(Sensor.class), anyInt()); + + /* Enable WiFi Client */ + mSarMgr.setClientWifiState(WifiManager.WIFI_STATE_ENABLED); + captureSarInfo(mWifiNative); + + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HEAD, mSarInfo.mSensorState); + assertFalse(mSarInfo.mIsVoiceCall); + + /* Start a Cell Call */ + mPhoneStateListener.onCallStateChanged(CALL_STATE_OFFHOOK, ""); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HEAD, mSarInfo.mSensorState); + assertTrue(mSarInfo.mIsVoiceCall); + + /* End the call */ + mPhoneStateListener.onCallStateChanged(CALL_STATE_IDLE, ""); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HEAD, mSarInfo.mSensorState); + assertFalse(mSarInfo.mIsVoiceCall); + } + + /** + * Test the error case when SarManager fails to register as a SensorEventListener. + * In this case, SarManager should assume operation near head all the time. + */ + @Test + public void testSarMgr_error_sar_register_failure() throws Exception { + createSarManagerSensorNegTest(SAR_SENSOR_NAME, true, false); + + verify(mSensorManager).registerListener(any(SensorEventListener.class), + any(Sensor.class), anyInt()); + + InOrder inOrder = inOrder(mWifiNative); + + /* Enable WiFi Client */ + mSarMgr.setClientWifiState(WifiManager.WIFI_STATE_ENABLED); + captureSarInfo(mWifiNative); + + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HEAD, mSarInfo.mSensorState); + assertFalse(mSarInfo.mIsVoiceCall); + + /* Start a Cell Call */ + mPhoneStateListener.onCallStateChanged(CALL_STATE_OFFHOOK, ""); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HEAD, mSarInfo.mSensorState); + assertTrue(mSarInfo.mIsVoiceCall); + + /* End the call */ + mPhoneStateListener.onCallStateChanged(CALL_STATE_IDLE, ""); + inOrder.verify(mWifiNative).selectTxPowerScenario(eq(mSarInfo)); + assertEquals(SarInfo.SAR_SENSOR_NEAR_HEAD, mSarInfo.mSensorState); + assertFalse(mSarInfo.mIsVoiceCall); } } |