• 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.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