summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--service/java/com/android/server/wifi/SarManager.java298
-rw-r--r--service/java/com/android/server/wifi/WifiInjector.java3
-rw-r--r--tests/wifitests/src/com/android/server/wifi/SarManagerTest.java493
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);
}
}