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 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 wifiDeviceStateChangeManager.registerStateChangeCallback( 110 new WifiDeviceStateChangeManager.StateChangeCallback() { 111 @Override 112 public void onScreenStateChanged(boolean screenOn) { 113 handleScreenStateChanged(screenOn); 114 } 115 }); 116 } 117 118 /** 119 * Handle boot completed, read config flags. 120 */ handleBootCompleted()121 public void handleBootCompleted() { 122 readSarConfigs(); 123 if (mSupportSarTxPowerLimit) { 124 mSarInfo = new SarInfo(); 125 setSarConfigsInInfo(); 126 registerListeners(); 127 } 128 } 129 130 /** 131 * Notify SarManager of screen status change 132 */ handleScreenStateChanged(boolean screenOn)133 private void handleScreenStateChanged(boolean screenOn) { 134 if (!mSupportSarVoiceCall) { 135 return; 136 } 137 138 if (mScreenOn == screenOn) { 139 return; 140 } 141 142 if (mVerboseLoggingEnabled) { 143 Log.d(TAG, "handleScreenStateChanged: screenOn = " + screenOn); 144 } 145 146 mScreenOn = screenOn; 147 148 // Only schedule a voice stream check if screen is turning on, and it is currently not 149 // scheduled 150 if (mScreenOn && !mIsVoiceStreamCheckEnabled) { 151 mHandler.post(() -> { 152 checkAudioDevice(); 153 }); 154 155 mIsVoiceStreamCheckEnabled = true; 156 } 157 } 158 isVoiceCallOnEarpiece()159 private boolean isVoiceCallOnEarpiece() { 160 final AudioAttributes voiceCallAttr = new AudioAttributes.Builder() 161 .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION) 162 .build(); 163 List<AudioDeviceAttributes> devices = mAudioManager.getDevicesForAttributes(voiceCallAttr); 164 for (AudioDeviceAttributes device : devices) { 165 if (device.getRole() == AudioDeviceAttributes.ROLE_OUTPUT 166 && device.getType() == AudioDeviceInfo.TYPE_BUILTIN_EARPIECE) { 167 return true; 168 } 169 } 170 return false; 171 } 172 isVoiceCallStreamActive()173 private boolean isVoiceCallStreamActive() { 174 int mode = mAudioManager.getMode(); 175 return mode == AudioManager.MODE_IN_COMMUNICATION || mode == AudioManager.MODE_IN_CALL; 176 } 177 checkAudioDevice()178 private void checkAudioDevice() { 179 // First Check if audio stream is on 180 boolean voiceStreamActive = isVoiceCallStreamActive(); 181 boolean earPieceActive; 182 183 if (voiceStreamActive) { 184 // Check on the audio route 185 earPieceActive = isVoiceCallOnEarpiece(); 186 187 if (mVerboseLoggingEnabled) { 188 Log.d(TAG, "EarPiece active = " + earPieceActive); 189 } 190 } else { 191 earPieceActive = false; 192 } 193 194 // If audio route has changed, update SAR 195 if (earPieceActive != mSarInfo.isEarPieceActive) { 196 mSarInfo.isEarPieceActive = earPieceActive; 197 updateSarScenario(); 198 } 199 200 // Now should we proceed with the checks 201 if (!mScreenOn && !voiceStreamActive) { 202 // No need to continue checking 203 mIsVoiceStreamCheckEnabled = false; 204 } else { 205 // Schedule another check 206 mHandler.postDelayed(() -> { 207 checkAudioDevice(); 208 }, CHECK_VOICE_STREAM_INTERVAL_MS); 209 } 210 } 211 readSarConfigs()212 private void readSarConfigs() { 213 mSupportSarTxPowerLimit = mContext.getResources().getBoolean( 214 R.bool.config_wifi_framework_enable_sar_tx_power_limit); 215 /* In case SAR is disabled, 216 then all SAR inputs are automatically disabled as well (irrespective of the config) */ 217 if (!mSupportSarTxPowerLimit) { 218 mSupportSarVoiceCall = false; 219 mSupportSarSoftAp = false; 220 return; 221 } 222 223 /* Voice calls are supported when SAR is supported */ 224 mSupportSarVoiceCall = true; 225 226 mSupportSarSoftAp = mContext.getResources().getBoolean( 227 R.bool.config_wifi_framework_enable_soft_ap_sar_tx_power_limit); 228 } 229 setSarConfigsInInfo()230 private void setSarConfigsInInfo() { 231 mSarInfo.sarVoiceCallSupported = mSupportSarVoiceCall; 232 mSarInfo.sarSapSupported = mSupportSarSoftAp; 233 } 234 registerListeners()235 private void registerListeners() { 236 if (mSupportSarVoiceCall) { 237 /* Listen for Phone State changes */ 238 registerPhoneStateListener(); 239 registerVoiceStreamListener(); 240 } 241 } 242 registerVoiceStreamListener()243 private void registerVoiceStreamListener() { 244 Log.i(TAG, "Registering for voice stream status"); 245 246 // Register for listening to transitions of change of voice stream devices 247 IntentFilter filter = new IntentFilter(); 248 filter.addAction(STREAM_DEVICES_CHANGED_ACTION); 249 250 mContext.registerReceiver( 251 new BroadcastReceiver() { 252 @Override 253 public void onReceive(Context context, Intent intent) { 254 boolean voiceStreamActive = isVoiceCallStreamActive(); 255 if (!voiceStreamActive) { 256 // No need to proceed, there is no voice call ongoing 257 return; 258 } 259 260 String action = intent.getAction(); 261 int streamType = 262 intent.getIntExtra(EXTRA_VOLUME_STREAM_TYPE, -1); 263 int device = intent.getIntExtra(EXTRA_VOLUME_STREAM_DEVICES, -1); 264 int oldDevice = intent.getIntExtra(EXTRA_PREV_VOLUME_STREAM_DEVICES, -1); 265 266 if (streamType == AudioManager.STREAM_VOICE_CALL) { 267 boolean earPieceActive = mSarInfo.isEarPieceActive; 268 if (device == DEVICE_OUT_EARPIECE) { 269 if (mVerboseLoggingEnabled) { 270 Log.d(TAG, "Switching to earpiece : HEAD ON"); 271 Log.d(TAG, "Old device = " + oldDevice); 272 } 273 earPieceActive = true; 274 } else if (oldDevice == DEVICE_OUT_EARPIECE) { 275 if (mVerboseLoggingEnabled) { 276 Log.d(TAG, "Switching from earpiece : HEAD OFF"); 277 Log.d(TAG, "New device = " + device); 278 } 279 earPieceActive = false; 280 } 281 282 if (earPieceActive != mSarInfo.isEarPieceActive) { 283 mSarInfo.isEarPieceActive = earPieceActive; 284 updateSarScenario(); 285 } 286 } 287 } 288 }, filter, null, mHandler); 289 } 290 291 /** 292 * Register the phone state listener. 293 */ registerPhoneStateListener()294 private void registerPhoneStateListener() { 295 Log.i(TAG, "Registering for telephony call state changes"); 296 mTelephonyManager.listen( 297 mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE); 298 } 299 300 /** 301 * Update Wifi Client State 302 */ setClientWifiState(int state)303 public void setClientWifiState(int state) { 304 boolean newIsEnabled; 305 /* No action is taken if SAR is not supported */ 306 if (!mSupportSarTxPowerLimit) { 307 return; 308 } 309 310 if (state == WifiManager.WIFI_STATE_DISABLED) { 311 newIsEnabled = false; 312 } else if (state == WifiManager.WIFI_STATE_ENABLED) { 313 newIsEnabled = true; 314 } else { 315 /* No change so exiting with no action */ 316 return; 317 } 318 319 /* Report change to HAL if needed */ 320 if (mSarInfo.isWifiClientEnabled != newIsEnabled) { 321 mSarInfo.isWifiClientEnabled = newIsEnabled; 322 updateSarScenario(); 323 } 324 } 325 326 /** 327 * Update Wifi SoftAP State 328 */ setSapWifiState(int state)329 public void setSapWifiState(int state) { 330 boolean newIsEnabled; 331 /* No action is taken if SAR is not supported */ 332 if (!mSupportSarTxPowerLimit) { 333 return; 334 } 335 336 if (state == WifiManager.WIFI_AP_STATE_DISABLED) { 337 newIsEnabled = false; 338 } else if (state == WifiManager.WIFI_AP_STATE_ENABLED) { 339 newIsEnabled = true; 340 } else { 341 /* No change so exiting with no action */ 342 return; 343 } 344 345 /* Report change to HAL if needed */ 346 if (mSarInfo.isWifiSapEnabled != newIsEnabled) { 347 mSarInfo.isWifiSapEnabled = newIsEnabled; 348 updateSarScenario(); 349 } 350 } 351 352 /** 353 * Update Wifi ScanOnly State 354 */ setScanOnlyWifiState(int state)355 public void setScanOnlyWifiState(int state) { 356 boolean newIsEnabled; 357 /* No action is taken if SAR is not supported */ 358 if (!mSupportSarTxPowerLimit) { 359 return; 360 } 361 362 if (state == WifiManager.WIFI_STATE_DISABLED) { 363 newIsEnabled = false; 364 } else if (state == WifiManager.WIFI_STATE_ENABLED) { 365 newIsEnabled = true; 366 } else { 367 /* No change so exiting with no action */ 368 return; 369 } 370 371 /* Report change to HAL if needed */ 372 if (mSarInfo.isWifiScanOnlyEnabled != newIsEnabled) { 373 mSarInfo.isWifiScanOnlyEnabled = newIsEnabled; 374 updateSarScenario(); 375 } 376 } 377 378 /** 379 * Report Cell state event 380 */ onCellStateChangeEvent(int state)381 private void onCellStateChangeEvent(int state) { 382 boolean newIsVoiceCall; 383 switch (state) { 384 case CALL_STATE_OFFHOOK: 385 case CALL_STATE_RINGING: 386 newIsVoiceCall = true; 387 break; 388 389 case CALL_STATE_IDLE: 390 newIsVoiceCall = false; 391 break; 392 393 default: 394 Log.e(TAG, "Invalid Cell State: " + state); 395 return; 396 } 397 398 /* Report change to HAL if needed */ 399 if (mSarInfo.isVoiceCall != newIsVoiceCall) { 400 mSarInfo.isVoiceCall = newIsVoiceCall; 401 402 if (mVerboseLoggingEnabled) { 403 Log.d(TAG, "Voice Call = " + newIsVoiceCall); 404 } 405 updateSarScenario(); 406 } 407 } 408 409 /** 410 * Enable/disable verbose logging. 411 */ enableVerboseLogging(boolean verboseEnabled)412 public void enableVerboseLogging(boolean verboseEnabled) { 413 mVerboseLoggingEnabled = verboseEnabled; 414 } 415 416 /** 417 * dump() 418 * Dumps SarManager state (as well as its SarInfo member variable state) 419 */ dump(FileDescriptor fd, PrintWriter pw, String[] args)420 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 421 pw.println("Dump of SarManager"); 422 pw.println("isSarSupported: " + mSupportSarTxPowerLimit); 423 pw.println("isSarVoiceCallSupported: " + mSupportSarVoiceCall); 424 pw.println("isSarSoftApSupported: " + mSupportSarSoftAp); 425 pw.println(""); 426 if (mSarInfo != null) { 427 mSarInfo.dump(fd, pw, args); 428 } 429 } 430 431 /** 432 * Listen for phone call state events to set/reset TX power limits for SAR requirements. 433 */ 434 private class WifiPhoneStateListener extends PhoneStateListener { WifiPhoneStateListener(Looper looper)435 WifiPhoneStateListener(Looper looper) { 436 super(new HandlerExecutor(new Handler(looper))); 437 } 438 439 /** 440 * onCallStateChanged() 441 * This callback is called when a call state event is received 442 * Note that this runs in the WifiCoreHandlerThread 443 * since the corresponding Looper was passed to the WifiPhoneStateListener constructor. 444 */ 445 @Override onCallStateChanged(int state, String incomingNumber)446 public void onCallStateChanged(int state, String incomingNumber) { 447 Log.d(TAG, "Received Phone State Change: " + state); 448 449 /* In case of an unsolicited event */ 450 if (!mSupportSarTxPowerLimit || !mSupportSarVoiceCall) { 451 return; 452 } 453 onCellStateChangeEvent(state); 454 } 455 } 456 457 /** 458 * updateSarScenario() 459 * Update HAL with the new SAR scenario if needed. 460 */ updateSarScenario()461 private void updateSarScenario() { 462 if (!mSarInfo.shouldReport()) { 463 return; 464 } 465 466 /* Report info to HAL*/ 467 if (mWifiNative.selectTxPowerScenario(mSarInfo)) { 468 mSarInfo.reportingSuccessful(); 469 } else { 470 Log.e(TAG, "Failed in WifiNative.selectTxPowerScenario()"); 471 } 472 473 return; 474 } 475 } 476