1/*
2 * Copyright (C) 2016 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 android.text.TextUtils;
20import android.util.Log;
21
22/**
23 * Provide functions for making changes to WiFi country code.
24 * This Country Code is from MCC or phone default setting. This class sends Country Code
25 * to driver through wpa_supplicant when WifiStateMachine marks current state as ready
26 * using setReadyForChange(true).
27 */
28public class WifiCountryCode {
29    private static final String TAG = "WifiCountryCode";
30    private final WifiNative mWifiNative;
31    private boolean DBG = false;
32    private boolean mReady = false;
33
34    /** config option that indicate whether or not to reset country code to default when
35     * cellular radio indicates country code loss
36     */
37    private boolean mRevertCountryCodeOnCellularLoss;
38    private String mDefaultCountryCode = null;
39    private String mTelephonyCountryCode = null;
40    private String mCurrentCountryCode = null;
41
42    public WifiCountryCode(
43            WifiNative wifiNative,
44            String oemDefaultCountryCode,
45            String persistentCountryCode,
46            boolean revertCountryCodeOnCellularLoss) {
47
48        mWifiNative = wifiNative;
49        mRevertCountryCodeOnCellularLoss = revertCountryCodeOnCellularLoss;
50
51        if (!TextUtils.isEmpty(persistentCountryCode)) {
52            mDefaultCountryCode = persistentCountryCode.toUpperCase();
53        } else if (!TextUtils.isEmpty(oemDefaultCountryCode)) {
54            mDefaultCountryCode = oemDefaultCountryCode.toUpperCase();
55        } else {
56            if (mRevertCountryCodeOnCellularLoss) {
57                Log.w(TAG, "config_wifi_revert_country_code_on_cellular_loss is set, "
58                         + "but there is no default country code.");
59                mRevertCountryCodeOnCellularLoss = false;
60                return;
61            }
62        }
63
64        if (mRevertCountryCodeOnCellularLoss) {
65            Log.d(TAG, "Country code will be reverted to " + mDefaultCountryCode
66                    + " on MCC loss");
67        }
68    }
69
70    /**
71     * Enable verbose logging for WifiCountryCode.
72     */
73    public void enableVerboseLogging(int verbose) {
74        if (verbose > 0) {
75            DBG = true;
76        } else {
77            DBG = false;
78        }
79    }
80
81    /**
82     * This is called when sim card is removed.
83     * In this case we should invalid all other country codes except the
84     * phone default one.
85     */
86    public synchronized void simCardRemoved() {
87        if (DBG) Log.d(TAG, "SIM Card Removed");
88        // SIM card is removed, we need to reset the country code to phone default.
89        if (mRevertCountryCodeOnCellularLoss) {
90            mTelephonyCountryCode = null;
91            if (mReady) {
92                updateCountryCode();
93            }
94        }
95    }
96
97    /**
98     * This is called when airplane mode is enabled.
99     * In this case we should invalidate all other country code except the
100     * phone default one.
101     */
102    public synchronized void airplaneModeEnabled() {
103        if (DBG) Log.d(TAG, "Airplane Mode Enabled");
104        mTelephonyCountryCode = null;
105        // Airplane mode is enabled, we need to reset the country code to phone default.
106        if (mRevertCountryCodeOnCellularLoss) {
107            mTelephonyCountryCode = null;
108            // Country code will be set upon when wpa_supplicant starts next time.
109        }
110    }
111
112    /**
113     * Change the state to indicates if wpa_supplicant is ready to handle country code changing
114     * request or not.
115     * We call native code to request country code changes only when wpa_supplicant is
116     * started but not yet L2 connected.
117     */
118    public synchronized void setReadyForChange(boolean ready) {
119        if (DBG) Log.d(TAG, "Set ready: " + ready);
120        mReady = ready;
121        // We are ready to set country code now.
122        // We need to post pending country code request.
123        if (mReady) {
124            updateCountryCode();
125        }
126    }
127
128    /**
129     * Handle country code change request.
130     * @param countryCode The country code intended to set.
131     * This is supposed to be from Telephony service.
132     * otherwise we think it is from other applications.
133     * @return Returns true if the country code passed in is acceptable.
134     */
135    public synchronized boolean setCountryCode(String countryCode, boolean persist) {
136        if (DBG) Log.d(TAG, "Receive set country code request: " + countryCode);
137        // Ignore empty country code.
138        if (TextUtils.isEmpty(countryCode)) {
139            if (DBG) Log.d(TAG, "Ignore empty country code");
140            return false;
141        }
142        if (persist) {
143            mDefaultCountryCode = countryCode;
144        }
145        mTelephonyCountryCode = countryCode.toUpperCase();
146        // If wpa_supplicant is ready we set the country code now, otherwise it will be
147        // set once wpa_supplicant is ready.
148        if (mReady) {
149            updateCountryCode();
150        }
151        return true;
152    }
153
154    /**
155     * Method to get the Country Code that was sent to wpa_supplicant.
156     *
157     * @return Returns the local copy of the Country Code that was sent to the driver upon
158     * setReadyForChange(true).
159     * If wpa_supplicant was never started, this may be null even if a SIM reported a valid
160     * country code.
161     * Returns null if no Country Code was sent to driver.
162     */
163    public synchronized String getCountryCodeSentToDriver() {
164        return mCurrentCountryCode;
165    }
166
167    /**
168     * Method to return the currently reported Country Code from the SIM or phone default setting.
169     *
170     * @return The currently reported Country Code from the SIM. If there is no Country Code
171     * reported from SIM, a phone default Country Code will be returned.
172     * Returns null when there is no Country Code available.
173     */
174    public synchronized String getCountryCode() {
175        return pickCountryCode();
176    }
177
178    private void updateCountryCode() {
179        if (DBG) Log.d(TAG, "Update country code");
180        String country = pickCountryCode();
181        // We do not check if the country code equals the current one.
182        // There are two reasons:
183        // 1. Wpa supplicant may silently modify the country code.
184        // 2. If Wifi restarted therefoere wpa_supplicant also restarted,
185        // the country code counld be reset to '00' by wpa_supplicant.
186        if (country != null) {
187            setCountryCodeNative(country);
188        }
189        // We do not set country code if there is no candidate. This is reasonable
190        // because wpa_supplicant usually starts with an international safe country
191        // code setting: '00'.
192    }
193
194    private String pickCountryCode() {
195        if (mTelephonyCountryCode != null) {
196            return mTelephonyCountryCode;
197        }
198        if (mDefaultCountryCode != null) {
199            return mDefaultCountryCode;
200        }
201        // If there is no candidate country code we will return null.
202        return null;
203    }
204
205    private boolean setCountryCodeNative(String country) {
206        if (mWifiNative.setCountryCode(country)) {
207            Log.d(TAG, "Succeeded to set country code to: " + country);
208            mCurrentCountryCode = country;
209            return true;
210        }
211        Log.d(TAG, "Failed to set country code to: " + country);
212        return false;
213    }
214}
215
216