1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.wifi; 18 19 import static android.telephony.TelephonyManager.CALL_STATE_IDLE; 20 import static android.telephony.TelephonyManager.CALL_STATE_OFFHOOK; 21 import static android.telephony.TelephonyManager.CALL_STATE_RINGING; 22 23 import android.content.BroadcastReceiver; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.IntentFilter; 27 import android.media.AudioAttributes; 28 import android.media.AudioDeviceAttributes; 29 import android.media.AudioDeviceInfo; 30 import android.media.AudioManager; 31 import android.net.wifi.WifiManager; 32 import android.os.Handler; 33 import android.os.Looper; 34 import android.telephony.PhoneStateListener; 35 import android.telephony.TelephonyManager; 36 import android.util.Log; 37 38 import com.android.modules.utils.HandlerExecutor; 39 import com.android.wifi.resources.R; 40 41 import java.io.FileDescriptor; 42 import java.io.PrintWriter; 43 import java.util.List; 44 45 /** 46 * This class provides the Support for SAR to control WiFi TX power limits. 47 * It deals with the following: 48 * - Tracking the STA state through calls from the ClientModeManager. 49 * - Tracking the SAP state through calls from SoftApManager 50 * - Tracking the Scan-Only state through ScanOnlyModeManager 51 * - Tracking the state of the Cellular calls or data. 52 * - It constructs the sar info and send it towards the HAL 53 */ 54 public class SarManager { 55 // Period for checking on voice steam active (in ms) 56 private static final int CHECK_VOICE_STREAM_INTERVAL_MS = 5000; 57 58 /** 59 * @hide constants copied over from {@link AudioManager} 60 * TODO(b/144250387): Migrate to public API 61 */ 62 private static final String STREAM_DEVICES_CHANGED_ACTION = 63 "android.media.STREAM_DEVICES_CHANGED_ACTION"; 64 private static final String EXTRA_VOLUME_STREAM_TYPE = "android.media.EXTRA_VOLUME_STREAM_TYPE"; 65 private static final String EXTRA_VOLUME_STREAM_DEVICES = 66 "android.media.EXTRA_VOLUME_STREAM_DEVICES"; 67 private static final String EXTRA_PREV_VOLUME_STREAM_DEVICES = 68 "android.media.EXTRA_PREV_VOLUME_STREAM_DEVICES"; 69 private static final int DEVICE_OUT_EARPIECE = 0x1; 70 71 /* For Logging */ 72 private static final String TAG = "WifiSarManager"; 73 private boolean mVerboseLoggingEnabled = true; 74 75 private final SarInfo mSarInfo; 76 77 /* Configuration for SAR support */ 78 private boolean mSupportSarTxPowerLimit; 79 private boolean mSupportSarVoiceCall; 80 private boolean mSupportSarSoftAp; 81 82 // Device starts with screen on 83 private boolean mScreenOn = false; 84 private boolean mIsVoiceStreamCheckEnabled = false; 85 86 /** 87 * Other parameters passed in or created in the constructor. 88 */ 89 private final Context mContext; 90 private final TelephonyManager mTelephonyManager; 91 private final AudioManager mAudioManager; 92 private final WifiPhoneStateListener mPhoneStateListener; 93 private final WifiNative mWifiNative; 94 private final Handler mHandler; 95 96 /** Create new instance of SarManager. */ SarManager( Context context, TelephonyManager telephonyManager, Looper looper, WifiNative wifiNative, WifiDeviceStateChangeManager wifiDeviceStateChangeManager)97 SarManager( 98 Context context, 99 TelephonyManager telephonyManager, 100 Looper looper, 101 WifiNative wifiNative, 102 WifiDeviceStateChangeManager wifiDeviceStateChangeManager) { 103 mContext = context; 104 mTelephonyManager = telephonyManager; 105 mWifiNative = wifiNative; 106 mAudioManager = mContext.getSystemService(AudioManager.class); 107 mHandler = new Handler(looper); 108 mPhoneStateListener = new WifiPhoneStateListener(looper); 109 mSarInfo = new SarInfo(); 110 wifiDeviceStateChangeManager.registerStateChangeCallback( 111 new WifiDeviceStateChangeManager.StateChangeCallback() { 112 @Override 113 public void onScreenStateChanged(boolean screenOn) { 114 handleScreenStateChanged(screenOn); 115 } 116 }); 117 } 118 119 /** 120 * Handle boot completed, read config flags. 121 */ handleBootCompleted()122 public void handleBootCompleted() { 123 readSarConfigs(); 124 if (mSupportSarTxPowerLimit) { 125 setSarConfigsInInfo(); 126 registerListeners(); 127 updateSarScenario(); 128 } 129 } 130 131 /** 132 * Notify SarManager of screen status change 133 */ handleScreenStateChanged(boolean screenOn)134 private void handleScreenStateChanged(boolean screenOn) { 135 if (!mSupportSarVoiceCall) { 136 return; 137 } 138 139 if (mScreenOn == screenOn) { 140 return; 141 } 142 143 if (mVerboseLoggingEnabled) { 144 Log.d(TAG, "handleScreenStateChanged: screenOn = " + screenOn); 145 } 146 147 mScreenOn = screenOn; 148 149 // Only schedule a voice stream check if screen is turning on, and it is currently not 150 // scheduled 151 if (mScreenOn && !mIsVoiceStreamCheckEnabled) { 152 mHandler.post(() -> { 153 checkAudioDevice(); 154 }); 155 156 mIsVoiceStreamCheckEnabled = true; 157 } 158 } 159 isVoiceCallOnEarpiece()160 private boolean isVoiceCallOnEarpiece() { 161 final AudioAttributes voiceCallAttr = new AudioAttributes.Builder() 162 .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION) 163 .build(); 164 List<AudioDeviceAttributes> devices = mAudioManager.getDevicesForAttributes(voiceCallAttr); 165 for (AudioDeviceAttributes device : devices) { 166 if (device.getRole() == AudioDeviceAttributes.ROLE_OUTPUT 167 && device.getType() == AudioDeviceInfo.TYPE_BUILTIN_EARPIECE) { 168 return true; 169 } 170 } 171 return false; 172 } 173 isVoiceCallStreamActive()174 private boolean isVoiceCallStreamActive() { 175 int mode = mAudioManager.getMode(); 176 return mode == AudioManager.MODE_IN_COMMUNICATION || mode == AudioManager.MODE_IN_CALL; 177 } 178 checkAudioDevice()179 private void checkAudioDevice() { 180 // First Check if audio stream is on 181 boolean voiceStreamActive = isVoiceCallStreamActive(); 182 boolean earPieceActive; 183 184 if (voiceStreamActive) { 185 // Check on the audio route 186 earPieceActive = isVoiceCallOnEarpiece(); 187 188 if (mVerboseLoggingEnabled) { 189 Log.d(TAG, "EarPiece active = " + earPieceActive); 190 } 191 } else { 192 earPieceActive = false; 193 } 194 195 // If audio route has changed, update SAR 196 if (earPieceActive != mSarInfo.isEarPieceActive) { 197 mSarInfo.isEarPieceActive = earPieceActive; 198 updateSarScenario(); 199 } 200 201 // Now should we proceed with the checks 202 if (!mScreenOn && !voiceStreamActive) { 203 // No need to continue checking 204 mIsVoiceStreamCheckEnabled = false; 205 } else { 206 // Schedule another check 207 mHandler.postDelayed(() -> { 208 checkAudioDevice(); 209 }, CHECK_VOICE_STREAM_INTERVAL_MS); 210 } 211 } 212 readSarConfigs()213 private void readSarConfigs() { 214 mSupportSarTxPowerLimit = mContext.getResources().getBoolean( 215 R.bool.config_wifi_framework_enable_sar_tx_power_limit); 216 /* In case SAR is disabled, 217 then all SAR inputs are automatically disabled as well (irrespective of the config) */ 218 if (!mSupportSarTxPowerLimit) { 219 mSupportSarVoiceCall = false; 220 mSupportSarSoftAp = false; 221 return; 222 } 223 224 /* Voice calls are supported when SAR is supported */ 225 mSupportSarVoiceCall = true; 226 227 mSupportSarSoftAp = mContext.getResources().getBoolean( 228 R.bool.config_wifi_framework_enable_soft_ap_sar_tx_power_limit); 229 } 230 setSarConfigsInInfo()231 private void setSarConfigsInInfo() { 232 mSarInfo.sarVoiceCallSupported = mSupportSarVoiceCall; 233 mSarInfo.sarSapSupported = mSupportSarSoftAp; 234 } 235 registerListeners()236 private void registerListeners() { 237 if (mSupportSarVoiceCall) { 238 /* Listen for Phone State changes */ 239 registerPhoneStateListener(); 240 registerVoiceStreamListener(); 241 } 242 } 243 registerVoiceStreamListener()244 private void registerVoiceStreamListener() { 245 Log.i(TAG, "Registering for voice stream status"); 246 247 // Register for listening to transitions of change of voice stream devices 248 IntentFilter filter = new IntentFilter(); 249 filter.addAction(STREAM_DEVICES_CHANGED_ACTION); 250 251 mContext.registerReceiver( 252 new BroadcastReceiver() { 253 @Override 254 public void onReceive(Context context, Intent intent) { 255 boolean voiceStreamActive = isVoiceCallStreamActive(); 256 if (!voiceStreamActive) { 257 // No need to proceed, there is no voice call ongoing 258 return; 259 } 260 261 String action = intent.getAction(); 262 int streamType = 263 intent.getIntExtra(EXTRA_VOLUME_STREAM_TYPE, -1); 264 int device = intent.getIntExtra(EXTRA_VOLUME_STREAM_DEVICES, -1); 265 int oldDevice = intent.getIntExtra(EXTRA_PREV_VOLUME_STREAM_DEVICES, -1); 266 267 if (streamType == AudioManager.STREAM_VOICE_CALL) { 268 boolean earPieceActive = mSarInfo.isEarPieceActive; 269 if (device == DEVICE_OUT_EARPIECE) { 270 if (mVerboseLoggingEnabled) { 271 Log.d(TAG, "Switching to earpiece : HEAD ON"); 272 Log.d(TAG, "Old device = " + oldDevice); 273 } 274 earPieceActive = true; 275 } else if (oldDevice == DEVICE_OUT_EARPIECE) { 276 if (mVerboseLoggingEnabled) { 277 Log.d(TAG, "Switching from earpiece : HEAD OFF"); 278 Log.d(TAG, "New device = " + device); 279 } 280 earPieceActive = false; 281 } 282 283 if (earPieceActive != mSarInfo.isEarPieceActive) { 284 mSarInfo.isEarPieceActive = earPieceActive; 285 updateSarScenario(); 286 } 287 } 288 } 289 }, filter, null, mHandler); 290 } 291 292 /** 293 * Register the phone state listener. 294 */ registerPhoneStateListener()295 private void registerPhoneStateListener() { 296 Log.i(TAG, "Registering for telephony call state changes"); 297 mTelephonyManager.listen( 298 mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE); 299 } 300 301 /** 302 * Update Wifi Client State 303 */ setClientWifiState(int state)304 public void setClientWifiState(int state) { 305 boolean newIsEnabled; 306 if (state == WifiManager.WIFI_STATE_DISABLED) { 307 newIsEnabled = false; 308 } else if (state == WifiManager.WIFI_STATE_ENABLED) { 309 newIsEnabled = true; 310 } else { 311 /* No change so exiting with no action */ 312 return; 313 } 314 315 /* Report change to HAL if needed */ 316 if (mSarInfo.isWifiClientEnabled != newIsEnabled) { 317 mSarInfo.isWifiClientEnabled = newIsEnabled; 318 updateSarScenario(); 319 } 320 } 321 322 /** 323 * Update Wifi SoftAP State 324 */ setSapWifiState(int state)325 public void setSapWifiState(int state) { 326 boolean newIsEnabled; 327 328 if (state == WifiManager.WIFI_AP_STATE_DISABLED) { 329 newIsEnabled = false; 330 } else if (state == WifiManager.WIFI_AP_STATE_ENABLED) { 331 newIsEnabled = true; 332 } else { 333 /* No change so exiting with no action */ 334 return; 335 } 336 337 /* Report change to HAL if needed */ 338 if (mSarInfo.isWifiSapEnabled != newIsEnabled) { 339 mSarInfo.isWifiSapEnabled = newIsEnabled; 340 updateSarScenario(); 341 } 342 } 343 344 /** 345 * Update Wifi ScanOnly State 346 */ setScanOnlyWifiState(int state)347 public void setScanOnlyWifiState(int state) { 348 boolean newIsEnabled; 349 350 if (state == WifiManager.WIFI_STATE_DISABLED) { 351 newIsEnabled = false; 352 } else if (state == WifiManager.WIFI_STATE_ENABLED) { 353 newIsEnabled = true; 354 } else { 355 /* No change so exiting with no action */ 356 return; 357 } 358 359 /* Report change to HAL if needed */ 360 if (mSarInfo.isWifiScanOnlyEnabled != newIsEnabled) { 361 mSarInfo.isWifiScanOnlyEnabled = newIsEnabled; 362 updateSarScenario(); 363 } 364 } 365 366 /** 367 * Report Cell state event 368 */ onCellStateChangeEvent(int state)369 private void onCellStateChangeEvent(int state) { 370 boolean newIsVoiceCall; 371 switch (state) { 372 case CALL_STATE_OFFHOOK: 373 case CALL_STATE_RINGING: 374 newIsVoiceCall = true; 375 break; 376 377 case CALL_STATE_IDLE: 378 newIsVoiceCall = false; 379 break; 380 381 default: 382 Log.e(TAG, "Invalid Cell State: " + state); 383 return; 384 } 385 386 /* Report change to HAL if needed */ 387 if (mSarInfo.isVoiceCall != newIsVoiceCall) { 388 mSarInfo.isVoiceCall = newIsVoiceCall; 389 390 if (mVerboseLoggingEnabled) { 391 Log.d(TAG, "Voice Call = " + newIsVoiceCall); 392 } 393 updateSarScenario(); 394 } 395 } 396 397 /** 398 * Enable/disable verbose logging. 399 */ enableVerboseLogging(boolean verboseEnabled)400 public void enableVerboseLogging(boolean verboseEnabled) { 401 mVerboseLoggingEnabled = verboseEnabled; 402 } 403 404 /** 405 * dump() 406 * Dumps SarManager state (as well as its SarInfo member variable state) 407 */ dump(FileDescriptor fd, PrintWriter pw, String[] args)408 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 409 pw.println("Dump of SarManager"); 410 pw.println("isSarSupported: " + mSupportSarTxPowerLimit); 411 pw.println("isSarVoiceCallSupported: " + mSupportSarVoiceCall); 412 pw.println("isSarSoftApSupported: " + mSupportSarSoftAp); 413 pw.println(""); 414 if (mSarInfo != null) { 415 mSarInfo.dump(fd, pw, args); 416 } 417 } 418 419 /** 420 * Listen for phone call state events to set/reset TX power limits for SAR requirements. 421 */ 422 private class WifiPhoneStateListener extends PhoneStateListener { WifiPhoneStateListener(Looper looper)423 WifiPhoneStateListener(Looper looper) { 424 super(new HandlerExecutor(new Handler(looper))); 425 } 426 427 /** 428 * onCallStateChanged() 429 * This callback is called when a call state event is received 430 * Note that this runs in the WifiCoreHandlerThread 431 * since the corresponding Looper was passed to the WifiPhoneStateListener constructor. 432 */ 433 @Override onCallStateChanged(int state, String incomingNumber)434 public void onCallStateChanged(int state, String incomingNumber) { 435 Log.d(TAG, "Received Phone State Change: " + state); 436 437 /* In case of an unsolicited event */ 438 if (!mSupportSarTxPowerLimit || !mSupportSarVoiceCall) { 439 return; 440 } 441 onCellStateChangeEvent(state); 442 } 443 } 444 445 /** 446 * updateSarScenario() 447 * Update HAL with the new SAR scenario if needed. 448 */ updateSarScenario()449 private void updateSarScenario() { 450 /* No action is taken if SAR is not supported */ 451 if (!mSupportSarTxPowerLimit) { 452 return; 453 } 454 if (!mSarInfo.shouldReport()) { 455 return; 456 } 457 458 /* Report info to HAL*/ 459 if (mWifiNative.selectTxPowerScenario(mSarInfo)) { 460 mSarInfo.reportingSuccessful(); 461 } else { 462 Log.e(TAG, "Failed in WifiNative.selectTxPowerScenario()"); 463 } 464 465 return; 466 } 467 } 468