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