• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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