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
17package com.android.server.wifi;
18
19import static android.telephony.TelephonyManager.CALL_STATE_IDLE;
20import static android.telephony.TelephonyManager.CALL_STATE_OFFHOOK;
21import static android.telephony.TelephonyManager.CALL_STATE_RINGING;
22
23import android.content.BroadcastReceiver;
24import android.content.Context;
25import android.content.Intent;
26import android.content.IntentFilter;
27import android.net.wifi.WifiManager;
28import android.os.Looper;
29import android.telephony.PhoneStateListener;
30import android.telephony.TelephonyManager;
31import android.util.Log;
32
33import com.android.internal.R;
34
35import java.io.FileDescriptor;
36import java.io.PrintWriter;
37import java.util.List;
38
39/**
40 * This class provides the Support for SAR to control WiFi TX power limits.
41 * It deals with the following:
42 * - Tracking the STA state through calls from  the ClientModeManager.
43 * - Tracking the state of the Cellular calls or data.
44 * - Based on above, selecting the SAR profile to use and programming it in wifi hal.
45 */
46public class SarManager {
47
48    /* For Logging */
49    private static final String TAG = "WifiSarManager";
50    private boolean mVerboseLoggingEnabled = true;
51
52    /* Configuration for SAR */
53    private boolean mEnableSarTxPowerLimit;
54
55    /* Current SAR Scenario */
56    private int mCurrentSarScenario = WifiNative.TX_POWER_SCENARIO_NORMAL;
57
58    /* Booleans for Cell and wifi states */
59    private boolean mCellOn = false;
60    private boolean mWifiStaEnabled = false;
61    /**
62     * Other parameters passed in or created in the constructor.
63     */
64    private final Context mContext;
65    private final TelephonyManager mTelephonyManager;
66    private final WifiPhoneStateListener mPhoneStateListener;
67    private final WifiNative mWifiNative;
68    private final Looper mLooper;
69
70    /**
71     * Create new instance of SarManager.
72     */
73    SarManager(Context context,
74               TelephonyManager telephonyManager,
75               Looper looper,
76               WifiNative wifiNative) {
77        mContext = context;
78        mTelephonyManager = telephonyManager;
79        mWifiNative = wifiNative;
80        mLooper = looper;
81        mPhoneStateListener = new WifiPhoneStateListener(looper);
82
83        registerListeners();
84    }
85
86    /**
87     * Starts the SAR Manager by initializing the different listeners
88     */
89    private void registerListeners() {
90        /* First read the configuration for SAR Support */
91        mEnableSarTxPowerLimit = mContext.getResources().getBoolean(
92                R.bool.config_wifi_framework_enable_voice_call_sar_tx_power_limit);
93
94        /* Only Start listening for events if SAR is enabled */
95        if (mEnableSarTxPowerLimit) {
96            Log.d(TAG, "Registering Listeners for the SAR Manager");
97
98            /* Listen for Phone State changes */
99            registerPhoneListener();
100        }
101    }
102
103    /**
104     * Report Cell state event
105     */
106    private void onCellStateChangeEvent(int state) {
107        boolean currentCellOn = mCellOn;
108
109        switch (state) {
110            case CALL_STATE_OFFHOOK:
111            case CALL_STATE_RINGING:
112                mCellOn = true;
113                break;
114
115            case CALL_STATE_IDLE:
116                mCellOn = false;
117                break;
118
119            default:
120                Log.e(TAG, "Invalid Cell State: " + state);
121        }
122
123        if (mCellOn != currentCellOn) {
124            updateSarScenario();
125        }
126    }
127
128    /**
129     * Update Wifi Client State
130     */
131    public void setClientWifiState(int state) {
132        /* No action is taken if SAR is not enabled */
133        if (!mEnableSarTxPowerLimit) return;
134
135        if (state == WifiManager.WIFI_STATE_DISABLED && mWifiStaEnabled) {
136            mWifiStaEnabled = false;
137        } else if (state == WifiManager.WIFI_STATE_ENABLED && !mWifiStaEnabled) {
138            mWifiStaEnabled = true;
139
140            /* Since no wifi interface was up,
141               time for SAR scenario to take effect */
142            sendTxPowerScenario(mCurrentSarScenario);
143        }
144    }
145
146    /**
147     * Enable/disable verbose logging.
148     */
149    public void enableVerboseLogging(int verbose) {
150        Log.d(TAG, "Inside enableVerboseLogging: " + verbose);
151        if (verbose > 0) {
152            mVerboseLoggingEnabled = true;
153        } else {
154            mVerboseLoggingEnabled = false;
155        }
156    }
157
158    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
159        pw.println("*** WiFi SAR Manager Dump ***");
160        pw.println("Current SAR Scenario is " + scenarioToString(mCurrentSarScenario));
161    }
162
163    /**
164     * Register the phone listener.
165     */
166    private void registerPhoneListener() {
167        Log.i(TAG, "Registering for telephony call state changes");
168        mTelephonyManager.listen(
169                mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
170    }
171
172    /**
173     * Listen for phone call state events to set/reset TX power limits for SAR requirements.
174     */
175    private class WifiPhoneStateListener extends PhoneStateListener {
176        WifiPhoneStateListener(Looper looper) {
177            super(looper);
178        }
179
180        @Override
181        public void onCallStateChanged(int state, String incomingNumber) {
182            Log.d(TAG, "Received Phone State Change: " + state);
183
184            /* In case of an unsolicited event */
185            if (!mEnableSarTxPowerLimit) return;
186
187            onCellStateChangeEvent(state);
188        }
189    }
190
191    /**
192     * update the Current SAR Scenario based on factors including:
193     * - Do we have an ongoing cellular voice call.
194     */
195    private void updateSarScenario() {
196        int newSarScenario;
197
198        if (mCellOn) {
199            newSarScenario = WifiNative.TX_POWER_SCENARIO_VOICE_CALL;
200        } else {
201            newSarScenario = WifiNative.TX_POWER_SCENARIO_NORMAL;
202        }
203
204        if (newSarScenario != mCurrentSarScenario) {
205
206            // Only update HAL with new scenario if WiFi interface is enabled
207            if (mWifiStaEnabled) {
208                Log.d(TAG, "Sending SAR Scenario #" + scenarioToString(newSarScenario));
209                sendTxPowerScenario(newSarScenario);
210            }
211
212            mCurrentSarScenario = newSarScenario;
213        }
214    }
215
216    /**
217     * sendTxPowerScenario()
218     * Update HAL with the new power scenario.
219     */
220    private void sendTxPowerScenario(int newSarScenario) {
221        if (!mWifiNative.selectTxPowerScenario(newSarScenario)) {
222            Log.e(TAG, "Failed to set TX power scenario");
223        }
224    }
225
226    /**
227     * Convert SAR Scenario to string
228     */
229    private String scenarioToString(int scenario) {
230        String str;
231        switch(scenario) {
232            case WifiNative.TX_POWER_SCENARIO_NORMAL:
233                str =  "TX_POWER_SCENARIO_NORMAL";
234                break;
235            case WifiNative.TX_POWER_SCENARIO_VOICE_CALL:
236                str = "TX_POWER_SCENARIO_VOICE_CALL";
237                break;
238            default:
239                str = "Invalid Scenario";
240                break;
241        }
242
243        return str;
244    }
245}
246