blob: 6879c41bc92ffa132f58ffca244b066689529051 [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.wifi;
import static 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.Context;
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;
import java.io.FileDescriptor;
import java.io.PrintWriter;
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.
* - 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;
/**
* Other parameters passed in or created in the constructor.
*/
private final Context mContext;
private final TelephonyManager mTelephonyManager;
private final WifiPhoneStateListener mPhoneStateListener;
private final WifiNative mWifiNative;
private final SarSensorEventListener mSensorListener;
private final SensorManager mSensorManager;
private final Looper mLooper;
private final WifiMetrics mWifiMetrics;
/**
* Create new instance of SarManager.
*/
SarManager(Context context,
TelephonyManager telephonyManager,
Looper looper,
WifiNative wifiNative,
SensorManager sensorManager,
WifiMetrics wifiMetrics) {
mContext = context;
mTelephonyManager = telephonyManager;
mWifiNative = wifiNative;
mLooper = looper;
mSensorManager = sensorManager;
mWifiMetrics = wifiMetrics;
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);
}
}
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");
mSarInfo.mSensorState = SarInfo.SAR_SENSOR_NEAR_HEAD;
mWifiMetrics.incrementNumSarSensorRegistrationFailures();
}
}
}
/**
* Register the phone state listener.
*/
private void registerPhoneStateListener() {
Log.i(TAG, "Registering for telephony call state changes");
mTelephonyManager.listen(
mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
}
/**
* Register the body/hand/head proximity sensor.
*/
private boolean registerSensorListener() {
Log.i(TAG, "Registering for Sensor notification Listener");
return mSensorListener.register();
}
/**
* 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();
}
}
/**
* 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();
}
}
/**
* Report Cell state event
*/
private void onCellStateChangeEvent(int state) {
boolean newIsVoiceCall;
switch (state) {
case CALL_STATE_OFFHOOK:
case CALL_STATE_RINGING:
newIsVoiceCall = true;
break;
case CALL_STATE_IDLE:
newIsVoiceCall = false;
break;
default:
Log.e(TAG, "Invalid Cell State: " + state);
return;
}
/* Report change to HAL if needed */
if (mSarInfo.mIsVoiceCall != newIsVoiceCall) {
mSarInfo.mIsVoiceCall = newIsVoiceCall;
updateSarScenario();
}
}
/**
* Report an event from the SAR sensor
*/
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;
}
/* Report change to HAL if needed */
if (mSarInfo.mSensorState != newSensorState) {
Log.d(TAG, "Setting Sensor state to " + SarInfo.sensorStateToString(newSensorState));
mSarInfo.mSensorState = newSensorState;
updateSarScenario();
}
}
/**
* Enable/disable verbose logging.
*/
public void enableVerboseLogging(int verbose) {
if (verbose > 0) {
mVerboseLoggingEnabled = true;
} else {
mVerboseLoggingEnabled = false;
}
}
/**
* dump()
* Dumps SarManager state (as well as its SarInfo member variable 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);
}
/**
* Listen for phone call state events to set/reset TX power limits for SAR requirements.
*/
private class WifiPhoneStateListener extends PhoneStateListener {
WifiPhoneStateListener(Looper looper) {
super(looper);
}
/**
* onCallStateChanged()
* This callback is called when a SAR sensor event is received
* Note that this runs in the WifiCoreHandlerThread
* 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;
}
onCellStateChangeEvent(state);
}
}
private class SarSensorEventListener implements SensorEventListener {
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;
}
/* 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;
}
/* Now register the listener */
if (!mSensorManager.registerListener(this, sensor,
SensorManager.SENSOR_DELAY_NORMAL)) {
Log.e(TAG, "Failed to register SAR Sensor Listener");
return false;
}
return true;
}
/**
* onSensorChanged()
* This callback is called when a SAR sensor event is received
* Note that this runs in the WifiCoreHandlerThread
* 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) {
}
}
/**
* updateSarScenario()
* Update HAL with the new SAR scenario if needed.
*/
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;
}
}