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.telephony.PhoneStateListener; 36 import android.telephony.TelephonyManager; 37 import android.util.Log; 38 39 import com.android.server.wifi.util.WifiHandler; 40 import com.android.wifi.resources.R; 41 42 import java.io.FileDescriptor; 43 import java.io.PrintWriter; 44 import java.util.List; 45 46 /** 47 * This class provides the Support for SAR to control WiFi TX power limits. 48 * It deals with the following: 49 * - Tracking the STA state through calls from the ClientModeManager. 50 * - Tracking the SAP state through calls from SoftApManager 51 * - Tracking the Scan-Only state through ScanOnlyModeManager 52 * - Tracking the state of the Cellular calls or data. 53 * - It constructs the sar info and send it towards the HAL 54 */ 55 public class SarManager { 56 // Period for checking on voice steam active (in ms) 57 private static final int CHECK_VOICE_STREAM_INTERVAL_MS = 5000; 58 59 /** 60 * @hide constants copied over from {@link AudioManager} 61 * TODO(b/144250387): Migrate to public API 62 */ 63 private static final String STREAM_DEVICES_CHANGED_ACTION = 64 "android.media.stream_devices_changed_action"; 65 private static final String EXTRA_VOLUME_STREAM_TYPE = "android.media.EXTRA_VOLUME_STREAM_TYPE"; 66 private static final String EXTRA_VOLUME_STREAM_DEVICES = 67 "android.media.EXTRA_VOLUME_STREAM_DEVICES"; 68 private static final String EXTRA_PREV_VOLUME_STREAM_DEVICES = 69 "android.media.EXTRA_PREV_VOLUME_STREAM_DEVICES"; 70 private static final int DEVICE_OUT_EARPIECE = 0x1; 71 72 /* For Logging */ 73 private static final String TAG = "WifiSarManager"; 74 private boolean mVerboseLoggingEnabled = true; 75 76 private SarInfo mSarInfo; 77 78 /* Configuration for SAR support */ 79 private boolean mSupportSarTxPowerLimit; 80 private boolean mSupportSarVoiceCall; 81 private boolean mSupportSarSoftAp; 82 83 // Device starts with screen on 84 private boolean mScreenOn = false; 85 private boolean mIsVoiceStreamCheckEnabled = false; 86 87 /** 88 * Other parameters passed in or created in the constructor. 89 */ 90 private final Context mContext; 91 private final TelephonyManager mTelephonyManager; 92 private final AudioManager mAudioManager; 93 private final WifiPhoneStateListener mPhoneStateListener; 94 private final WifiNative mWifiNative; 95 private final Handler mHandler; 96 private final Looper mLooper; 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 mLooper = looper; 109 mAudioManager = mContext.getSystemService(AudioManager.class); 110 mHandler = new WifiHandler(TAG, looper); 111 mPhoneStateListener = new WifiPhoneStateListener(looper); 112 } 113 114 /** 115 * Handle boot completed, read config flags. 116 */ handleBootCompleted()117 public void handleBootCompleted() { 118 readSarConfigs(); 119 if (mSupportSarTxPowerLimit) { 120 mSarInfo = new SarInfo(); 121 setSarConfigsInInfo(); 122 registerListeners(); 123 } 124 } 125 126 /** 127 * Notify SarManager of screen status change 128 */ handleScreenStateChanged(boolean screenOn)129 public void handleScreenStateChanged(boolean screenOn) { 130 if (!mSupportSarVoiceCall) { 131 return; 132 } 133 134 if (mScreenOn == screenOn) { 135 return; 136 } 137 138 if (mVerboseLoggingEnabled) { 139 Log.d(TAG, "handleScreenStateChanged: screenOn = " + screenOn); 140 } 141 142 mScreenOn = screenOn; 143 144 // Only schedule a voice stream check if screen is turning on, and it is currently not 145 // scheduled 146 if (mScreenOn && !mIsVoiceStreamCheckEnabled) { 147 mHandler.post(() -> { 148 checkAudioDevice(); 149 }); 150 151 mIsVoiceStreamCheckEnabled = true; 152 } 153 } 154 isVoiceCallOnEarpiece()155 private boolean isVoiceCallOnEarpiece() { 156 final AudioAttributes voiceCallAttr = new AudioAttributes.Builder() 157 .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION) 158 .build(); 159 List<AudioDeviceAttributes> devices = mAudioManager.getDevicesForAttributes(voiceCallAttr); 160 for (AudioDeviceAttributes device : devices) { 161 if (device.getRole() == AudioDeviceAttributes.ROLE_OUTPUT 162 && device.getType() == AudioDeviceInfo.TYPE_BUILTIN_EARPIECE) { 163 return true; 164 } 165 } 166 return false; 167 } 168 isVoiceCallStreamActive()169 private boolean isVoiceCallStreamActive() { 170 int mode = mAudioManager.getMode(); 171 return mode == AudioManager.MODE_IN_COMMUNICATION || mode == AudioManager.MODE_IN_CALL; 172 } 173 checkAudioDevice()174 private void checkAudioDevice() { 175 // First Check if audio stream is on 176 boolean voiceStreamActive = isVoiceCallStreamActive(); 177 boolean earPieceActive; 178 179 if (voiceStreamActive) { 180 // Check on the audio route 181 earPieceActive = isVoiceCallOnEarpiece(); 182 183 if (mVerboseLoggingEnabled) { 184 Log.d(TAG, "EarPiece active = " + earPieceActive); 185 } 186 } else { 187 earPieceActive = false; 188 } 189 190 // If audio route has changed, update SAR 191 if (earPieceActive != mSarInfo.isEarPieceActive) { 192 mSarInfo.isEarPieceActive = earPieceActive; 193 updateSarScenario(); 194 } 195 196 // Now should we proceed with the checks 197 if (!mScreenOn && !voiceStreamActive) { 198 // No need to continue checking 199 mIsVoiceStreamCheckEnabled = false; 200 } else { 201 // Schedule another check 202 mHandler.postDelayed(() -> { 203 checkAudioDevice(); 204 }, CHECK_VOICE_STREAM_INTERVAL_MS); 205 } 206 } 207 readSarConfigs()208 private void readSarConfigs() { 209 mSupportSarTxPowerLimit = mContext.getResources().getBoolean( 210 R.bool.config_wifi_framework_enable_sar_tx_power_limit); 211 /* In case SAR is disabled, 212 then all SAR inputs are automatically disabled as well (irrespective of the config) */ 213 if (!mSupportSarTxPowerLimit) { 214 mSupportSarVoiceCall = false; 215 mSupportSarSoftAp = false; 216 return; 217 } 218 219 /* Voice calls are supported when SAR is supported */ 220 mSupportSarVoiceCall = true; 221 222 mSupportSarSoftAp = mContext.getResources().getBoolean( 223 R.bool.config_wifi_framework_enable_soft_ap_sar_tx_power_limit); 224 } 225 setSarConfigsInInfo()226 private void setSarConfigsInInfo() { 227 mSarInfo.sarVoiceCallSupported = mSupportSarVoiceCall; 228 mSarInfo.sarSapSupported = mSupportSarSoftAp; 229 } 230 registerListeners()231 private void registerListeners() { 232 if (mSupportSarVoiceCall) { 233 /* Listen for Phone State changes */ 234 registerPhoneStateListener(); 235 registerVoiceStreamListener(); 236 } 237 } 238 registerVoiceStreamListener()239 private void registerVoiceStreamListener() { 240 Log.i(TAG, "Registering for voice stream status"); 241 242 // Register for listening to transitions of change of voice stream devices 243 IntentFilter filter = new IntentFilter(); 244 filter.addAction(STREAM_DEVICES_CHANGED_ACTION); 245 246 mContext.registerReceiver( 247 new BroadcastReceiver() { 248 @Override 249 public void onReceive(Context context, Intent intent) { 250 boolean voiceStreamActive = isVoiceCallStreamActive(); 251 if (!voiceStreamActive) { 252 // No need to proceed, there is no voice call ongoing 253 return; 254 } 255 256 String action = intent.getAction(); 257 int streamType = 258 intent.getIntExtra(EXTRA_VOLUME_STREAM_TYPE, -1); 259 int device = intent.getIntExtra(EXTRA_VOLUME_STREAM_DEVICES, -1); 260 int oldDevice = intent.getIntExtra(EXTRA_PREV_VOLUME_STREAM_DEVICES, -1); 261 262 if (streamType == AudioManager.STREAM_VOICE_CALL) { 263 boolean earPieceActive = mSarInfo.isEarPieceActive; 264 if (device == DEVICE_OUT_EARPIECE) { 265 if (mVerboseLoggingEnabled) { 266 Log.d(TAG, "Switching to earpiece : HEAD ON"); 267 Log.d(TAG, "Old device = " + oldDevice); 268 } 269 earPieceActive = true; 270 } else if (oldDevice == DEVICE_OUT_EARPIECE) { 271 if (mVerboseLoggingEnabled) { 272 Log.d(TAG, "Switching from earpiece : HEAD OFF"); 273 Log.d(TAG, "New device = " + device); 274 } 275 earPieceActive = false; 276 } 277 278 if (earPieceActive != mSarInfo.isEarPieceActive) { 279 mSarInfo.isEarPieceActive = earPieceActive; 280 updateSarScenario(); 281 } 282 } 283 } 284 }, filter, null, mHandler); 285 } 286 287 /** 288 * Register the phone state listener. 289 */ registerPhoneStateListener()290 private void registerPhoneStateListener() { 291 Log.i(TAG, "Registering for telephony call state changes"); 292 mTelephonyManager.listen( 293 mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE); 294 } 295 296 /** 297 * Update Wifi Client State 298 */ setClientWifiState(int state)299 public void setClientWifiState(int state) { 300 boolean newIsEnabled; 301 /* No action is taken if SAR is not supported */ 302 if (!mSupportSarTxPowerLimit) { 303 return; 304 } 305 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 /* No action is taken if SAR is not supported */ 328 if (!mSupportSarTxPowerLimit) { 329 return; 330 } 331 332 if (state == WifiManager.WIFI_AP_STATE_DISABLED) { 333 newIsEnabled = false; 334 } else if (state == WifiManager.WIFI_AP_STATE_ENABLED) { 335 newIsEnabled = true; 336 } else { 337 /* No change so exiting with no action */ 338 return; 339 } 340 341 /* Report change to HAL if needed */ 342 if (mSarInfo.isWifiSapEnabled != newIsEnabled) { 343 mSarInfo.isWifiSapEnabled = newIsEnabled; 344 updateSarScenario(); 345 } 346 } 347 348 /** 349 * Update Wifi ScanOnly State 350 */ setScanOnlyWifiState(int state)351 public void setScanOnlyWifiState(int state) { 352 boolean newIsEnabled; 353 /* No action is taken if SAR is not supported */ 354 if (!mSupportSarTxPowerLimit) { 355 return; 356 } 357 358 if (state == WifiManager.WIFI_STATE_DISABLED) { 359 newIsEnabled = false; 360 } else if (state == WifiManager.WIFI_STATE_ENABLED) { 361 newIsEnabled = true; 362 } else { 363 /* No change so exiting with no action */ 364 return; 365 } 366 367 /* Report change to HAL if needed */ 368 if (mSarInfo.isWifiScanOnlyEnabled != newIsEnabled) { 369 mSarInfo.isWifiScanOnlyEnabled = newIsEnabled; 370 updateSarScenario(); 371 } 372 } 373 374 /** 375 * Report Cell state event 376 */ onCellStateChangeEvent(int state)377 private void onCellStateChangeEvent(int state) { 378 boolean newIsVoiceCall; 379 switch (state) { 380 case CALL_STATE_OFFHOOK: 381 case CALL_STATE_RINGING: 382 newIsVoiceCall = true; 383 break; 384 385 case CALL_STATE_IDLE: 386 newIsVoiceCall = false; 387 break; 388 389 default: 390 Log.e(TAG, "Invalid Cell State: " + state); 391 return; 392 } 393 394 /* Report change to HAL if needed */ 395 if (mSarInfo.isVoiceCall != newIsVoiceCall) { 396 mSarInfo.isVoiceCall = newIsVoiceCall; 397 398 if (mVerboseLoggingEnabled) { 399 Log.d(TAG, "Voice Call = " + newIsVoiceCall); 400 } 401 updateSarScenario(); 402 } 403 } 404 405 /** 406 * Enable/disable verbose logging. 407 */ enableVerboseLogging(int verbose)408 public void enableVerboseLogging(int verbose) { 409 if (verbose > 0) { 410 mVerboseLoggingEnabled = true; 411 } else { 412 mVerboseLoggingEnabled = false; 413 } 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