summaryrefslogtreecommitdiff
path: root/service
diff options
context:
space:
mode:
Diffstat (limited to 'service')
-rw-r--r--service/java/com/android/server/wifi/SarInfo.java194
-rw-r--r--service/java/com/android/server/wifi/SarManager.java353
-rw-r--r--service/java/com/android/server/wifi/ScanOnlyModeManager.java7
-rw-r--r--service/java/com/android/server/wifi/SoftApManager.java11
-rw-r--r--service/java/com/android/server/wifi/WifiInjector.java8
-rw-r--r--service/java/com/android/server/wifi/WifiNative.java17
-rw-r--r--service/java/com/android/server/wifi/WifiVendorHal.java227
7 files changed, 683 insertions, 134 deletions
diff --git a/service/java/com/android/server/wifi/SarInfo.java b/service/java/com/android/server/wifi/SarInfo.java
new file mode 100644
index 000000000..6eb777c65
--- /dev/null
+++ b/service/java/com/android/server/wifi/SarInfo.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * This class represents the list of SAR inputs that will be used to select the proper
+ * power profile.
+ * This includes:
+ * - SAR body sensor status
+ * - Is there an ongoing voice call
+ * - Is SoftAP active
+ * It also contains info about state of the other Wifi modes
+ * - Client mode (Sta)
+ * - ScanOnly mode
+ * It also keeps history for the reporting of SAR states/scenario to avoid unnecessary reporting
+ * - keeps track of the last reported states
+ * - keeps track of the last reported SAR scenario
+ * - keeps track of if all wifi modes were disabled (no reporting should happen then)
+ */
+public class SarInfo {
+ /**
+ * This value is used as an initial value for the last reported scenario
+ * It is intended to be different than all valid SAR scenario values (including the
+ * reset value).
+ * Using this to initialize the lastReportedScenario results in that the first scenario
+ * (including reset) would be reported.
+ */
+ public static final int INITIAL_SAR_SCENARIO = -2;
+
+ /**
+ * This value is used for the reset scenario (no TX Power backoff)
+ * Valid scenario values only include scenarios with Tx Power backoff,
+ * so we need this one to represent the "No backoff" case.
+ */
+ public static final int RESET_SAR_SCENARIO = -1;
+
+ private static final String SAR_SENSOR_FREE_SPACE_STR = "SAR_SENSOR_FREE_SPACE";
+ private static final String SAR_SENSOR_NEAR_BODY_STR = "SAR_SENSOR_NEAR_BODY";
+ private static final String SAR_SENSOR_NEAR_HAND_STR = "SAR_SENSOR_NEAR_HAND";
+ private static final String SAR_SENSOR_NEAR_HEAD_STR = "SAR_SENSOR_NEAR_HEAD";
+
+ public static final int SAR_SENSOR_FREE_SPACE = 1;
+ public static final int SAR_SENSOR_NEAR_HAND = 2;
+ public static final int SAR_SENSOR_NEAR_HEAD = 3;
+ public static final int SAR_SENSOR_NEAR_BODY = 4;
+
+ /* For Logging */
+ private static final String TAG = "WifiSarInfo";
+
+ public boolean mSarSensorEnabled;
+
+ public int mSensorState = SAR_SENSOR_FREE_SPACE;
+ public boolean mIsWifiClientEnabled = false;
+ public boolean mIsWifiSapEnabled = false;
+ public boolean mIsWifiScanOnlyEnabled = false;
+ public boolean mIsVoiceCall = false;
+ public int mAttemptedSarScenario = RESET_SAR_SCENARIO;
+
+ private boolean mAllWifiDisabled = true;
+
+ /* Variables representing the last successfully reported values to hal */
+ private int mLastReportedSensorState = SAR_SENSOR_FREE_SPACE;
+ private boolean mLastReportedIsWifiSapEnabled = false;
+ private boolean mLastReportedIsVoiceCall = false;
+ private int mLastReportedScenario = INITIAL_SAR_SCENARIO;
+
+ SarInfo(boolean sarSensorEnabled) {
+ mSarSensorEnabled = sarSensorEnabled;
+ }
+
+ /**
+ * shouldReport()
+ * This method returns false in the following cases:
+ * 1. If all Wifi modes are disabled.
+ * 2. Values contributing to the SAR scenario selection have not changed
+ * since last successful reporting.
+ *
+ * Special cases to allow for devices that require setting the SAR scenario value
+ * when the chip comes up (initial startup, or during operation)
+ * 1. This method would report true even with unchanged values from last reporting,
+ * if any wifi mode is just enabled after all wifi modes were disabled.
+ * 2. This method would report true the first time it is called with any wifi mode enabled.
+ */
+ public boolean shouldReport() {
+ /* Check if all Wifi modes are disabled */
+ if (!mIsWifiClientEnabled && !mIsWifiSapEnabled && !mIsWifiScanOnlyEnabled) {
+ mAllWifiDisabled = true;
+ return false;
+ }
+
+ /* Check if Wifi was all disabled before this call */
+ if (mAllWifiDisabled) {
+ return true;
+ }
+
+ /* Check if some change happened since last successful reporting */
+ if ((mSensorState != mLastReportedSensorState)
+ || (mIsWifiSapEnabled != mLastReportedIsWifiSapEnabled)
+ || (mIsVoiceCall != mLastReportedIsVoiceCall)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * reportingSuccessful()
+ * This method is called when reporting SAR scenario is fully successful
+ * This results in caching the last reported inputs for future comparison.
+ */
+ public void reportingSuccessful() {
+ mLastReportedSensorState = mSensorState;
+ mLastReportedIsWifiSapEnabled = mIsWifiSapEnabled;
+ mLastReportedIsVoiceCall = mIsVoiceCall;
+ mLastReportedScenario = mAttemptedSarScenario;
+
+ mAllWifiDisabled = false;
+ }
+
+ /**
+ * resetSarScenarioNeeded()
+ * Returns true if a call towards HAL to reset SAR scenario would be necessary.
+ * Returns false if the last call to HAL was already a reset, and hence
+ * another call to reset the SAR scenario would be redundant.
+ */
+ public boolean resetSarScenarioNeeded() {
+ return setSarScenarioNeeded(RESET_SAR_SCENARIO);
+ }
+
+ /**
+ * setSarScenarioNeeded()
+ * Returns true if a call towards HAL to set SAR scenario to that value would be
+ * necessary.
+ * Returns false if the last call to HAL was to set the scenario to that value, hence,
+ * another call to set the SAR scenario to the same value would be redundant.
+ */
+ public boolean setSarScenarioNeeded(int scenario) {
+ mAttemptedSarScenario = scenario;
+ return (mLastReportedScenario != scenario);
+ }
+
+ /**
+ * dump()
+ * Dumps the state of SarInfo
+ */
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("*** WiFi SAR Info Dump ***");
+ pw.println("Current values:");
+ pw.println(" Sensor state is: " + sensorStateToString(mSensorState));
+ pw.println(" Voice Call state is: " + mIsVoiceCall);
+ pw.println(" Wifi Client state is: " + mIsWifiClientEnabled);
+ pw.println(" Wifi Soft AP state is: " + mIsWifiSapEnabled);
+ pw.println(" Wifi ScanOnly state is: " + mIsWifiScanOnlyEnabled);
+ pw.println("Last reported values:");
+ pw.println(" Sensor state is: " + sensorStateToString(mLastReportedSensorState));
+ pw.println(" Soft AP state is: " + mLastReportedIsWifiSapEnabled);
+ pw.println(" Voice Call state is: " + mLastReportedIsVoiceCall);
+ }
+
+ /**
+ * Convert SAR sensor state to string
+ */
+ public static String sensorStateToString(int sensorState) {
+ switch(sensorState) {
+ case SAR_SENSOR_FREE_SPACE:
+ return SAR_SENSOR_FREE_SPACE_STR;
+ case SAR_SENSOR_NEAR_BODY:
+ return SAR_SENSOR_NEAR_BODY_STR;
+ case SAR_SENSOR_NEAR_HAND:
+ return SAR_SENSOR_NEAR_HAND_STR;
+ case SAR_SENSOR_NEAR_HEAD:
+ return SAR_SENSOR_NEAR_HEAD_STR;
+ default:
+ return "Invalid SAR sensor state";
+ }
+ }
+}
diff --git a/service/java/com/android/server/wifi/SarManager.java b/service/java/com/android/server/wifi/SarManager.java
index da48a8537..d38eab981 100644
--- a/service/java/com/android/server/wifi/SarManager.java
+++ b/service/java/com/android/server/wifi/SarManager.java
@@ -20,14 +20,16 @@ import static android.telephony.TelephonyManager.CALL_STATE_IDLE;
import static android.telephony.TelephonyManager.CALL_STATE_OFFHOOK;
import static android.telephony.TelephonyManager.CALL_STATE_RINGING;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
import android.net.wifi.WifiManager;
import android.os.Looper;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
+import android.text.TextUtils;
import android.util.Log;
import com.android.internal.R;
@@ -40,24 +42,28 @@ import java.util.List;
* This class provides the Support for SAR to control WiFi TX power limits.
* It deals with the following:
* - Tracking the STA state through calls from the ClientModeManager.
+ * - Tracking the SAP state through calls from SoftApManager
+ * - Tracking the Scan-Only state through ScanOnlyModeManager
* - Tracking the state of the Cellular calls or data.
- * - Based on above, selecting the SAR profile to use and programming it in wifi hal.
+ * - Tracking the sensor indicating proximity to user head/hand/body.
+ * - It constructs the sar info and send it towards the HAL
*/
public class SarManager {
-
/* For Logging */
private static final String TAG = "WifiSarManager";
private boolean mVerboseLoggingEnabled = true;
+ private SarInfo mSarInfo;
+
/* Configuration for SAR */
private boolean mEnableSarTxPowerLimit;
+ private boolean mEnableSarBodyProximity;
+ /* Sensor event definitions */
+ private int mSarSensorEventFreeSpace;
+ private int mSarSensorEventNearBody;
+ private int mSarSensorEventNearHand;
+ private int mSarSensorEventNearHead;
- /* Current SAR Scenario */
- private int mCurrentSarScenario = WifiNative.TX_POWER_SCENARIO_NORMAL;
-
- /* Booleans for Cell and wifi states */
- private boolean mCellOn = false;
- private boolean mWifiStaEnabled = false;
/**
* Other parameters passed in or created in the constructor.
*/
@@ -65,6 +71,8 @@ public class SarManager {
private final TelephonyManager mTelephonyManager;
private final WifiPhoneStateListener mPhoneStateListener;
private final WifiNative mWifiNative;
+ private final SarSensorEventListener mSensorListener;
+ private final SensorManager mSensorManager;
private final Looper mLooper;
/**
@@ -73,30 +81,157 @@ public class SarManager {
SarManager(Context context,
TelephonyManager telephonyManager,
Looper looper,
- WifiNative wifiNative) {
+ WifiNative wifiNative,
+ SensorManager sensorManager) {
mContext = context;
mTelephonyManager = telephonyManager;
mWifiNative = wifiNative;
mLooper = looper;
+ mSensorManager = sensorManager;
mPhoneStateListener = new WifiPhoneStateListener(looper);
+ mSensorListener = new SarSensorEventListener();
+
+ readSarConfigs();
+ if (mEnableSarTxPowerLimit) {
+ mSarInfo = new SarInfo(mEnableSarBodyProximity);
+ registerListeners();
+ }
+ }
+
+ private void readSarConfigs() {
+ mEnableSarTxPowerLimit = mContext.getResources().getBoolean(
+ R.bool.config_wifi_framework_enable_sar_tx_power_limit);
+ /* In case SAR is disabled,
+ then SAR sensor is automatically disabled as well (irrespective of the config) */
+ if (!mEnableSarTxPowerLimit) {
+ mEnableSarBodyProximity = false;
+ return;
+ }
+
+ mEnableSarBodyProximity = mContext.getResources().getBoolean(
+ R.bool.config_wifi_framework_enable_body_proximity_sar_tx_power_limit);
+
+ /* Read the sar sensor event Ids */
+ if (mEnableSarBodyProximity) {
+ mSarSensorEventFreeSpace = mContext.getResources().getInteger(
+ R.integer.config_wifi_framework_sar_free_space_event_id);
+ mSarSensorEventNearBody = mContext.getResources().getInteger(
+ R.integer.config_wifi_framework_sar_near_body_event_id);
+ mSarSensorEventNearHand = mContext.getResources().getInteger(
+ R.integer.config_wifi_framework_sar_near_hand_event_id);
+ mSarSensorEventNearHead = mContext.getResources().getInteger(
+ R.integer.config_wifi_framework_sar_near_head_event_id);
+ }
+ }
- registerListeners();
+ private void registerListeners() {
+ /* Listen for Phone State changes */
+ registerPhoneStateListener();
+
+ /* Only listen for SAR sensor if supported */
+ if (mEnableSarBodyProximity) {
+ /* Register the SAR sensor listener.
+ * If this fails, we will assume worst case (near head) */
+ if (!registerSensorListener()) {
+ Log.e(TAG, "Failed to register sensor listener, setting Sensor to NearHead");
+ /*TODO Need to add a metric to determine how often this happens */
+ mSarInfo.mSensorState = SarInfo.SAR_SENSOR_NEAR_HEAD;
+ }
+ }
}
/**
- * Starts the SAR Manager by initializing the different listeners
+ * Register the phone state listener.
*/
- private void registerListeners() {
- /* First read the configuration for SAR Support */
- mEnableSarTxPowerLimit = mContext.getResources().getBoolean(
- R.bool.config_wifi_framework_enable_voice_call_sar_tx_power_limit);
+ private void registerPhoneStateListener() {
+ Log.i(TAG, "Registering for telephony call state changes");
+ mTelephonyManager.listen(
+ mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
+ }
- /* Only Start listening for events if SAR is enabled */
- if (mEnableSarTxPowerLimit) {
- Log.d(TAG, "Registering Listeners for the SAR Manager");
+ /**
+ * Register the body/hand/head proximity sensor.
+ */
+ private boolean registerSensorListener() {
+ Log.i(TAG, "Registering for Sensor notification Listener");
+ return mSensorListener.register();
+ }
+
+ /**
+ * Update Wifi Client State
+ */
+ public void setClientWifiState(int state) {
+ boolean newIsEnabled;
+ /* No action is taken if SAR is not enabled */
+ if (!mEnableSarTxPowerLimit) {
+ return;
+ }
+
+ if (state == WifiManager.WIFI_STATE_DISABLED) {
+ newIsEnabled = false;
+ } else if (state == WifiManager.WIFI_STATE_ENABLED) {
+ newIsEnabled = true;
+ } else {
+ /* No change so exiting with no action */
+ return;
+ }
+
+ /* Report change to HAL if needed */
+ if (mSarInfo.mIsWifiClientEnabled != newIsEnabled) {
+ mSarInfo.mIsWifiClientEnabled = newIsEnabled;
+ updateSarScenario();
+ }
+ }
+
+ /**
+ * Update Wifi SoftAP State
+ */
+ public void setSapWifiState(int state) {
+ boolean newIsEnabled;
+ /* No action is taken if SAR is not enabled */
+ if (!mEnableSarTxPowerLimit) {
+ return;
+ }
+
+ if (state == WifiManager.WIFI_AP_STATE_DISABLED) {
+ newIsEnabled = false;
+ } else if (state == WifiManager.WIFI_AP_STATE_ENABLED) {
+ newIsEnabled = true;
+ } else {
+ /* No change so exiting with no action */
+ return;
+ }
+
+ /* Report change to HAL if needed */
+ if (mSarInfo.mIsWifiSapEnabled != newIsEnabled) {
+ mSarInfo.mIsWifiSapEnabled = newIsEnabled;
+ updateSarScenario();
+ }
+ }
- /* Listen for Phone State changes */
- registerPhoneListener();
+ /**
+ * Update Wifi ScanOnly State
+ */
+ public void setScanOnlyWifiState(int state) {
+ boolean newIsEnabled;
+ /* No action is taken if SAR is not enabled */
+ if (!mEnableSarTxPowerLimit) {
+ return;
+ }
+
+ if (state == WifiManager.WIFI_STATE_DISABLED) {
+ newIsEnabled = false;
+ } else if (state == WifiManager.WIFI_STATE_ENABLED) {
+ newIsEnabled = true;
+ } else {
+ /* No change so exiting with no action */
+ return;
+ }
+
+ /* Report change to HAL if needed */
+ if (mSarInfo.mIsWifiScanOnlyEnabled != newIsEnabled) {
+ mSarInfo.mIsWifiScanOnlyEnabled = newIsEnabled;
+ updateSarScenario();
}
}
@@ -104,42 +239,52 @@ public class SarManager {
* Report Cell state event
*/
private void onCellStateChangeEvent(int state) {
- boolean currentCellOn = mCellOn;
-
+ boolean newIsVoiceCall;
switch (state) {
case CALL_STATE_OFFHOOK:
case CALL_STATE_RINGING:
- mCellOn = true;
+ newIsVoiceCall = true;
break;
case CALL_STATE_IDLE:
- mCellOn = false;
+ newIsVoiceCall = false;
break;
default:
Log.e(TAG, "Invalid Cell State: " + state);
+ return;
}
- if (mCellOn != currentCellOn) {
+ /* Report change to HAL if needed */
+ if (mSarInfo.mIsVoiceCall != newIsVoiceCall) {
+ mSarInfo.mIsVoiceCall = newIsVoiceCall;
updateSarScenario();
}
}
/**
- * Update Wifi Client State
+ * Report an event from the SAR sensor
*/
- public void setClientWifiState(int state) {
- /* No action is taken if SAR is not enabled */
- if (!mEnableSarTxPowerLimit) return;
-
- if (state == WifiManager.WIFI_STATE_DISABLED && mWifiStaEnabled) {
- mWifiStaEnabled = false;
- } else if (state == WifiManager.WIFI_STATE_ENABLED && !mWifiStaEnabled) {
- mWifiStaEnabled = true;
+ private void onSarSensorEvent(int sarSensorEvent) {
+ int newSensorState;
+ if (sarSensorEvent == mSarSensorEventFreeSpace) {
+ newSensorState = SarInfo.SAR_SENSOR_FREE_SPACE;
+ } else if (sarSensorEvent == mSarSensorEventNearBody) {
+ newSensorState = SarInfo.SAR_SENSOR_NEAR_BODY;
+ } else if (sarSensorEvent == mSarSensorEventNearHand) {
+ newSensorState = SarInfo.SAR_SENSOR_NEAR_HAND;
+ } else if (sarSensorEvent == mSarSensorEventNearHead) {
+ newSensorState = SarInfo.SAR_SENSOR_NEAR_HEAD;
+ } else {
+ Log.e(TAG, "Invalid SAR sensor event id: " + sarSensorEvent);
+ return;
+ }
- /* Since no wifi interface was up,
- time for SAR scenario to take effect */
- sendTxPowerScenario(mCurrentSarScenario);
+ /* Report change to HAL if needed */
+ if (mSarInfo.mSensorState != newSensorState) {
+ Log.d(TAG, "Setting Sensor state to " + SarInfo.sensorStateToString(newSensorState));
+ mSarInfo.mSensorState = newSensorState;
+ updateSarScenario();
}
}
@@ -147,7 +292,6 @@ public class SarManager {
* Enable/disable verbose logging.
*/
public void enableVerboseLogging(int verbose) {
- Log.d(TAG, "Inside enableVerboseLogging: " + verbose);
if (verbose > 0) {
mVerboseLoggingEnabled = true;
} else {
@@ -155,18 +299,16 @@ public class SarManager {
}
}
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println("*** WiFi SAR Manager Dump ***");
- pw.println("Current SAR Scenario is " + scenarioToString(mCurrentSarScenario));
- }
-
/**
- * Register the phone listener.
+ * dump()
+ * Dumps SarManager state (as well as its SarInfo member variable state)
*/
- private void registerPhoneListener() {
- Log.i(TAG, "Registering for telephony call state changes");
- mTelephonyManager.listen(
- mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("*** WiFi SAR Manager Dump ***");
+ pw.println("isSarEnabled: " + mEnableSarTxPowerLimit);
+ pw.println("isSarSensorEnabled: " + mEnableSarBodyProximity);
+ pw.println("");
+ mSarInfo.dump(fd, pw, args);
}
/**
@@ -177,69 +319,96 @@ public class SarManager {
super(looper);
}
+ /**
+ * onCallStateChanged()
+ * This callback is called when a SAR sensor event is received
+ * Note that this runs in the WifiStateMachineHandlerThread
+ * since the corresponding Looper was passed to the WifiPhoneStateListener constructor.
+ */
@Override
public void onCallStateChanged(int state, String incomingNumber) {
Log.d(TAG, "Received Phone State Change: " + state);
/* In case of an unsolicited event */
- if (!mEnableSarTxPowerLimit) return;
-
+ if (!mEnableSarTxPowerLimit) {
+ return;
+ }
onCellStateChangeEvent(state);
}
}
- /**
- * update the Current SAR Scenario based on factors including:
- * - Do we have an ongoing cellular voice call.
- */
- private void updateSarScenario() {
- int newSarScenario;
+ private class SarSensorEventListener implements SensorEventListener {
- if (mCellOn) {
- newSarScenario = WifiNative.TX_POWER_SCENARIO_VOICE_CALL;
- } else {
- newSarScenario = WifiNative.TX_POWER_SCENARIO_NORMAL;
- }
+ private Sensor mSensor;
- if (newSarScenario != mCurrentSarScenario) {
+ /**
+ * Register the SAR listener to get SAR sensor events
+ */
+ private boolean register() {
+ /* Get the sensor type from configuration */
+ String sensorType = mContext.getResources().getString(
+ R.string.config_wifi_sar_sensor_type);
+ if (TextUtils.isEmpty(sensorType)) {
+ Log.e(TAG, "Empty SAR sensor type");
+ return false;
+ }
+
+ /* Get the sensor object */
+ Sensor sensor = null;
+ List<Sensor> sensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL);
+ for (Sensor s : sensorList) {
+ if (sensorType.equals(s.getStringType())) {
+ sensor = s;
+ break;
+ }
+ }
+ if (sensor == null) {
+ Log.e(TAG, "Failed to Find the SAR Sensor");
+ return false;
+ }
- // Only update HAL with new scenario if WiFi interface is enabled
- if (mWifiStaEnabled) {
- Log.d(TAG, "Sending SAR Scenario #" + scenarioToString(newSarScenario));
- sendTxPowerScenario(newSarScenario);
+ /* Now register the listener */
+ if (!mSensorManager.registerListener(this, sensor,
+ SensorManager.SENSOR_DELAY_NORMAL)) {
+ Log.e(TAG, "Failed to register SAR Sensor Listener");
+ return false;
}
- mCurrentSarScenario = newSarScenario;
+ return true;
}
- }
- /**
- * sendTxPowerScenario()
- * Update HAL with the new power scenario.
- */
- private void sendTxPowerScenario(int newSarScenario) {
- if (!mWifiNative.selectTxPowerScenario(newSarScenario)) {
- Log.e(TAG, "Failed to set TX power scenario");
+ /**
+ * onSensorChanged()
+ * This callback is called when a SAR sensor event is received
+ * Note that this runs in the WifiStateMachineHandlerThread
+ * since, the corresponding Looper was passed to the SensorManager instance.
+ */
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ onSarSensorEvent((int) event.values[0]);
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
}
/**
- * Convert SAR Scenario to string
+ * updateSarScenario()
+ * Update HAL with the new SAR scenario if needed.
*/
- private String scenarioToString(int scenario) {
- String str;
- switch(scenario) {
- case WifiNative.TX_POWER_SCENARIO_NORMAL:
- str = "TX_POWER_SCENARIO_NORMAL";
- break;
- case WifiNative.TX_POWER_SCENARIO_VOICE_CALL:
- str = "TX_POWER_SCENARIO_VOICE_CALL";
- break;
- default:
- str = "Invalid Scenario";
- break;
+ private void updateSarScenario() {
+ if (!mSarInfo.shouldReport()) {
+ return;
+ }
+
+ /* Report info to HAL*/
+ if (mWifiNative.selectTxPowerScenario(mSarInfo)) {
+ mSarInfo.reportingSuccessful();
+ } else {
+ Log.e(TAG, "Failed in WifiNative.selectTxPowerScenario()");
}
- return str;
+ return;
}
}
diff --git a/service/java/com/android/server/wifi/ScanOnlyModeManager.java b/service/java/com/android/server/wifi/ScanOnlyModeManager.java
index 346d2ca67..991657929 100644
--- a/service/java/com/android/server/wifi/ScanOnlyModeManager.java
+++ b/service/java/com/android/server/wifi/ScanOnlyModeManager.java
@@ -48,6 +48,7 @@ public class ScanOnlyModeManager implements ActiveModeManager {
private final Listener mListener;
private final ScanRequestProxy mScanRequestProxy;
private final WakeupController mWakeupController;
+ private final SarManager mSarManager;
private String mClientInterfaceName;
private boolean mIfaceIsUp = false;
@@ -58,13 +59,15 @@ public class ScanOnlyModeManager implements ActiveModeManager {
@NonNull WifiNative wifiNative, @NonNull Listener listener,
@NonNull WifiMetrics wifiMetrics,
@NonNull ScanRequestProxy scanRequestProxy,
- @NonNull WakeupController wakeupController) {
+ @NonNull WakeupController wakeupController,
+ @NonNull SarManager sarManager) {
mContext = context;
mWifiNative = wifiNative;
mListener = listener;
mWifiMetrics = wifiMetrics;
mScanRequestProxy = scanRequestProxy;
mWakeupController = wakeupController;
+ mSarManager = sarManager;
mStateMachine = new ScanOnlyModeStateMachine(looper);
}
@@ -242,6 +245,7 @@ public class ScanOnlyModeManager implements ActiveModeManager {
mIfaceIsUp = false;
onUpChanged(mWifiNative.isInterfaceUp(mClientInterfaceName));
+ mSarManager.setScanOnlyWifiState(WifiManager.WIFI_STATE_ENABLED);
}
@Override
@@ -281,6 +285,7 @@ public class ScanOnlyModeManager implements ActiveModeManager {
mClientInterfaceName = null;
}
updateWifiState(WifiManager.WIFI_STATE_DISABLED);
+ mSarManager.setScanOnlyWifiState(WifiManager.WIFI_STATE_DISABLED);
// once we leave started, nothing else to do... stop the state machine
mStateMachine.quitNow();
diff --git a/service/java/com/android/server/wifi/SoftApManager.java b/service/java/com/android/server/wifi/SoftApManager.java
index 6c52918a5..1afe9ed9d 100644
--- a/service/java/com/android/server/wifi/SoftApManager.java
+++ b/service/java/com/android/server/wifi/SoftApManager.java
@@ -93,6 +93,8 @@ public class SoftApManager implements ActiveModeManager {
private int mNumAssociatedStations = 0;
private boolean mTimeoutEnabled = false;
+ private final SarManager mSarManager;
+
/**
* Listener for soft AP events.
*/
@@ -118,7 +120,8 @@ public class SoftApManager implements ActiveModeManager {
@NonNull WifiManager.SoftApCallback callback,
@NonNull WifiApConfigStore wifiApConfigStore,
@NonNull SoftApModeConfiguration apConfig,
- @NonNull WifiMetrics wifiMetrics) {
+ @NonNull WifiMetrics wifiMetrics,
+ @NonNull SarManager sarManager) {
mContext = context;
mFrameworkFacade = framework;
mWifiNative = wifiNative;
@@ -133,6 +136,7 @@ public class SoftApManager implements ActiveModeManager {
mApConfig = config;
}
mWifiMetrics = wifiMetrics;
+ mSarManager = sarManager;
mStateMachine = new SoftApStateMachine(looper);
}
@@ -491,6 +495,9 @@ public class SoftApManager implements ActiveModeManager {
if (mSettingObserver != null) {
mSettingObserver.register();
}
+
+ mSarManager.setSapWifiState(WifiManager.WIFI_AP_STATE_ENABLED);
+
Log.d(TAG, "Resetting num stations on start");
mNumAssociatedStations = 0;
scheduleTimeoutMessage();
@@ -512,6 +519,8 @@ public class SoftApManager implements ActiveModeManager {
mWifiMetrics.addSoftApUpChangedEvent(false, mMode);
updateApState(WifiManager.WIFI_AP_STATE_DISABLED,
WifiManager.WIFI_AP_STATE_DISABLING, 0);
+
+ mSarManager.setSapWifiState(WifiManager.WIFI_AP_STATE_DISABLED);
mApInterfaceName = null;
mIfaceIsUp = false;
mStateMachine.quitNow();
diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java
index 0e30af841..199548090 100644
--- a/service/java/com/android/server/wifi/WifiInjector.java
+++ b/service/java/com/android/server/wifi/WifiInjector.java
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.content.Context;
+import android.hardware.SystemSensorManager;
import android.net.NetworkKey;
import android.net.NetworkScoreManager;
import android.net.wifi.IWifiScanner;
@@ -253,7 +254,7 @@ public class WifiInjector {
this, mWifiConfigManager,
mWifiPermissionsUtil, mWifiMetrics, mClock);
mSarManager = new SarManager(mContext, makeTelephonyManager(), wifiStateMachineLooper,
- mWifiNative);
+ mWifiNative, new SystemSensorManager(mContext, wifiStateMachineLooper));
if (mUseRealLogger) {
mWifiDiagnostics = new WifiDiagnostics(
mContext, this, mWifiNative, mBuildProperties,
@@ -464,7 +465,7 @@ public class WifiInjector {
@NonNull SoftApModeConfiguration config) {
return new SoftApManager(mContext, mWifiStateMachineHandlerThread.getLooper(),
mFrameworkFacade, mWifiNative, mCountryCode.getCountryCode(), callback,
- mWifiApConfigStore, config, mWifiMetrics);
+ mWifiApConfigStore, config, mWifiMetrics, mSarManager);
}
/**
@@ -476,7 +477,8 @@ public class WifiInjector {
public ScanOnlyModeManager makeScanOnlyModeManager(
@NonNull ScanOnlyModeManager.Listener listener) {
return new ScanOnlyModeManager(mContext, mWifiStateMachineHandlerThread.getLooper(),
- mWifiNative, listener, mWifiMetrics, mScanRequestProxy, mWakeupController);
+ mWifiNative, listener, mWifiMetrics, mScanRequestProxy, mWakeupController,
+ mSarManager);
}
/**
diff --git a/service/java/com/android/server/wifi/WifiNative.java b/service/java/com/android/server/wifi/WifiNative.java
index 0f785873b..bc599c141 100644
--- a/service/java/com/android/server/wifi/WifiNative.java
+++ b/service/java/com/android/server/wifi/WifiNative.java
@@ -2820,21 +2820,14 @@ public class WifiNative {
}
/**
- * Tx power level scenarios that can be selected.
- */
- public static final int TX_POWER_SCENARIO_NORMAL = 0;
- public static final int TX_POWER_SCENARIO_VOICE_CALL = 1;
-
- /**
- * Select one of the pre-configured TX power level scenarios or reset it back to normal.
- * Primarily used for meeting SAR requirements during voice calls.
+ * Select one of the pre-configured transmit power level scenarios or reset it back to normal.
+ * Primarily used for meeting SAR requirements.
*
- * @param scenario Should be one {@link #TX_POWER_SCENARIO_NORMAL} or
- * {@link #TX_POWER_SCENARIO_VOICE_CALL}.
+ * @param sarInfo The collection of inputs used to select the SAR scenario.
* @return true for success; false for failure or if the HAL version does not support this API.
*/
- public boolean selectTxPowerScenario(int scenario) {
- return mWifiVendorHal.selectTxPowerScenario(scenario);
+ public boolean selectTxPowerScenario(SarInfo sarInfo) {
+ return mWifiVendorHal.selectTxPowerScenario(sarInfo);
}
/********************************************************
diff --git a/service/java/com/android/server/wifi/WifiVendorHal.java b/service/java/com/android/server/wifi/WifiVendorHal.java
index 0d73459b5..361387e2f 100644
--- a/service/java/com/android/server/wifi/WifiVendorHal.java
+++ b/service/java/com/android/server/wifi/WifiVendorHal.java
@@ -2653,13 +2653,95 @@ public class WifiVendorHal {
return android.hardware.wifi.V1_2.IWifiStaIface.castFrom(iface);
}
+ /**
+ * sarPowerBackoffRequired_1_1()
+ * This method checks if we need to backoff wifi Tx power due to SAR requirements.
+ * It handles the case when the device is running the V1_1 version of WifiChip HAL
+ * In that HAL version, it is required to perform wifi Tx power backoff only if
+ * a voice call is ongoing.
+ */
+ private boolean sarPowerBackoffRequired_1_1(SarInfo sarInfo) {
+ /* As long as no voice call is active, no backoff is needed */
+ return sarInfo.mIsVoiceCall;
+ }
+
+ /**
+ * frameworkToHalTxPowerScenario_1_1()
+ * This method maps the information inside the SarInfo instance into a SAR scenario
+ * when device is running the V1_1 version of WifiChip HAL.
+ * In this HAL version, only one scenario is defined which is for VOICE_CALL
+ * otherwise, an exception is thrown.
+ */
+ private int frameworkToHalTxPowerScenario_1_1(SarInfo sarInfo) {
+ if (sarInfo.mIsVoiceCall) {
+ return android.hardware.wifi.V1_1.IWifiChip.TxPowerScenario.VOICE_CALL;
+ } else {
+ throw new IllegalArgumentException("bad scenario: voice call not active");
+ }
+ }
- private int frameworkToHalTxPowerScenario(int scenario) {
- switch (scenario) {
- case WifiNative.TX_POWER_SCENARIO_VOICE_CALL:
+ /**
+ * sarPowerBackoffRequired_1_2()
+ * This method checks if we need to backoff wifi Tx power due to SAR requirements.
+ * It handles the case when the device is running the V1_2 version of WifiChip HAL
+ * In that HAL version, behavior depends on if SAR sensor input is considered in this device.
+ * If it is, then whenever the device is near the user body/hand/head, back-off is required.
+ * Otherwise, we should revert to the V1_1 HAL behavior which is only to perform backoff when
+ * a voice call is ongoing.
+ */
+ private boolean sarPowerBackoffRequired_1_2(SarInfo sarInfo) {
+ if (sarInfo.mSarSensorEnabled) {
+ return (sarInfo.mSensorState != SarInfo.SAR_SENSOR_FREE_SPACE);
+ } else {
+ return sarInfo.mIsVoiceCall;
+ }
+ }
+
+ /**
+ * frameworkToHalTxPowerScenario_1_2()
+ * This method maps the information inside the SarInfo instance into a SAR scenario
+ * when device is running the V1_2 version of WifiChip HAL.
+ * In this HAL version, behavior depends on if SAR sensor input is considered in this device.
+ * If it is, then based on regulatory compliance requirements,
+ * - There is no need to treat NEAR_HAND different from NEAR_BODY, both can be considered
+ * near the user body.
+ * - Running in softAP mode can be treated the same way as running a voice call from tx power
+ * backoff perspective.
+ * If SAR sensor input is not considered in this device, then we should revert to the V1_1 HAL
+ * behavior, and the only valid scenario would be when a voice call is ongoing.
+ */
+ private int frameworkToHalTxPowerScenario_1_2(SarInfo sarInfo) {
+ if (sarInfo.mSarSensorEnabled) {
+ switch(sarInfo.mSensorState) {
+ case SarInfo.SAR_SENSOR_NEAR_BODY:
+ case SarInfo.SAR_SENSOR_NEAR_HAND:
+ if (sarInfo.mIsVoiceCall || sarInfo.mIsWifiSapEnabled) {
+ return android.hardware.wifi.V1_2.IWifiChip
+ .TxPowerScenario.ON_BODY_CELL_ON;
+ } else {
+ return android.hardware.wifi.V1_2.IWifiChip
+ .TxPowerScenario.ON_BODY_CELL_OFF;
+ }
+
+ case SarInfo.SAR_SENSOR_NEAR_HEAD:
+ if (sarInfo.mIsVoiceCall || sarInfo.mIsWifiSapEnabled) {
+ return android.hardware.wifi.V1_2.IWifiChip
+ .TxPowerScenario.ON_HEAD_CELL_ON;
+ } else {
+ return android.hardware.wifi.V1_2.IWifiChip
+ .TxPowerScenario.ON_HEAD_CELL_OFF;
+ }
+
+ default:
+ throw new IllegalArgumentException("bad scenario: Invalid sensor state");
+ }
+ } else {
+ /* SAR Sensors not enabled, act like V1_1 */
+ if (sarInfo.mIsVoiceCall) {
return android.hardware.wifi.V1_1.IWifiChip.TxPowerScenario.VOICE_CALL;
- default:
- throw new IllegalArgumentException("bad scenario: " + scenario);
+ } else {
+ throw new IllegalArgumentException("bad scenario: voice call not active");
+ }
}
}
@@ -2667,35 +2749,130 @@ public class WifiVendorHal {
* Select one of the pre-configured TX power level scenarios or reset it back to normal.
* Primarily used for meeting SAR requirements during voice calls.
*
- * @param scenario Should be one {@link WifiNative#TX_POWER_SCENARIO_NORMAL} or
- * {@link WifiNative#TX_POWER_SCENARIO_VOICE_CALL}.
+ * Note: If it was found out that the scenario to be reported is the same as last reported one,
+ * then exit with success.
+ * This is to handle the case when some HAL versions deal with different inputs equally,
+ * in that case, we should not call the hal unless there is a change in scenario.
+ * Note: It is assumed that this method is only called if SAR is enabled. The logic of whether
+ * to call it or not resides in SarManager class.
+ * Note: This method is called whether SAR sensor is supported or not. The passed SarInfo object
+ * contains a flag to indicate the SAR sensor support.
+ *
+ * @param sarInfo The collection of inputs to select the SAR scenario.
* @return true for success; false for failure or if the HAL version does not support this API.
*/
- public boolean selectTxPowerScenario(int scenario) {
+ public boolean selectTxPowerScenario(SarInfo sarInfo) {
synchronized (sLock) {
- try {
- android.hardware.wifi.V1_1.IWifiChip iWifiChipV11 = getWifiChipForV1_1Mockable();
- if (iWifiChipV11 == null) return boolResult(false);
- WifiStatus status;
- if (scenario != WifiNative.TX_POWER_SCENARIO_NORMAL) {
- int halScenario;
- try {
- halScenario = frameworkToHalTxPowerScenario(scenario);
- } catch (IllegalArgumentException e) {
- mLog.err("Illegal argument for select tx power scenario")
- .c(e.toString()).flush();
+ // First attempt to get a V_1_2 instance of the Wifi HAL.
+ android.hardware.wifi.V1_2.IWifiChip iWifiChipV12 = getWifiChipForV1_2Mockable();
+ if (iWifiChipV12 != null) {
+ return selectTxPowerScenario_1_2(iWifiChipV12, sarInfo);
+ }
+
+ // Now attempt to get a V_1_1 instance of the Wifi HAL.
+ android.hardware.wifi.V1_1.IWifiChip iWifiChipV11 = getWifiChipForV1_1Mockable();
+ if (iWifiChipV11 != null) {
+ return selectTxPowerScenario_1_1(iWifiChipV11, sarInfo);
+ }
+
+ // HAL version does not support SAR
+ return false;
+ }
+ }
+
+ private boolean selectTxPowerScenario_1_1(
+ android.hardware.wifi.V1_1.IWifiChip iWifiChip, SarInfo sarInfo) {
+ WifiStatus status;
+ try {
+ if (sarPowerBackoffRequired_1_1(sarInfo)) {
+ // Power backoff is needed, so calculate the required scenario,
+ // and attempt to set it.
+ int halScenario = frameworkToHalTxPowerScenario_1_1(sarInfo);
+ if (sarInfo.setSarScenarioNeeded(halScenario)) {
+ status = iWifiChip.selectTxPowerScenario(halScenario);
+ if (ok(status)) {
+ mLog.e("Setting SAR scenario to " + halScenario);
+ return true;
+ } else {
+ mLog.e("Failed to set SAR scenario to " + halScenario);
return false;
}
- status = iWifiChipV11.selectTxPowerScenario(halScenario);
+ }
+
+ // Reaching here means setting SAR scenario would be redundant,
+ // do nothing and return with success.
+ return true;
+ }
+
+ // We don't need to perform power backoff, so attempt to reset SAR scenario.
+ if (sarInfo.resetSarScenarioNeeded()) {
+ status = iWifiChip.resetTxPowerScenario();
+ if (ok(status)) {
+ mLog.d("Resetting SAR scenario");
+ return true;
} else {
- status = iWifiChipV11.resetTxPowerScenario();
+ mLog.e("Failed to reset SAR scenario");
+ return false;
}
- if (!ok(status)) return false;
- } catch (RemoteException e) {
- handleRemoteException(e);
- return false;
}
+
+ // Resetting SAR scenario would be redundant,
+ // do nothing and return with success.
return true;
+ } catch (RemoteException e) {
+ handleRemoteException(e);
+ return false;
+ } catch (IllegalArgumentException e) {
+ mLog.err("Illegal argument for selectTxPowerScenario_1_1()").c(e.toString()).flush();
+ return false;
+ }
+ }
+
+ private boolean selectTxPowerScenario_1_2(
+ android.hardware.wifi.V1_2.IWifiChip iWifiChip, SarInfo sarInfo) {
+ WifiStatus status;
+ try {
+ if (sarPowerBackoffRequired_1_2(sarInfo)) {
+ // Power backoff is needed, so calculate the required scenario,
+ // and attempt to set it.
+ int halScenario = frameworkToHalTxPowerScenario_1_2(sarInfo);
+ if (sarInfo.setSarScenarioNeeded(halScenario)) {
+ status = iWifiChip.selectTxPowerScenario_1_2(halScenario);
+ if (ok(status)) {
+ mLog.e("Setting SAR scenario to " + halScenario);
+ return true;
+ } else {
+ mLog.e("Failed to set SAR scenario to " + halScenario);
+ return false;
+ }
+ }
+
+ // Reaching here means setting SAR scenario would be redundant,
+ // do nothing and return with success.
+ return true;
+ }
+
+ // We don't need to perform power backoff, so attempt to reset SAR scenario.
+ if (sarInfo.resetSarScenarioNeeded()) {
+ status = iWifiChip.resetTxPowerScenario();
+ if (ok(status)) {
+ mLog.d("Resetting SAR scenario");
+ return true;
+ } else {
+ mLog.e("Failed to reset SAR scenario");
+ return false;
+ }
+ }
+
+ // Resetting SAR scenario would be redundant,
+ // do nothing and return with success.
+ return true;
+ } catch (RemoteException e) {
+ handleRemoteException(e);
+ return false;
+ } catch (IllegalArgumentException e) {
+ mLog.err("Illegal argument for selectTxPowerScenario_1_2()").c(e.toString()).flush();
+ return false;
}
}