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