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