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