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 private boolean mForceCountryCode = false; 52 WifiCountryCode( WifiNative wifiNative, String oemDefaultCountryCode, boolean revertCountryCodeOnCellularLoss)53 public WifiCountryCode( 54 WifiNative wifiNative, 55 String oemDefaultCountryCode, 56 boolean revertCountryCodeOnCellularLoss) { 57 58 mWifiNative = wifiNative; 59 mRevertCountryCodeOnCellularLoss = revertCountryCodeOnCellularLoss; 60 61 if (!TextUtils.isEmpty(oemDefaultCountryCode)) { 62 mDefaultCountryCode = oemDefaultCountryCode.toUpperCase(Locale.US); 63 } else { 64 if (mRevertCountryCodeOnCellularLoss) { 65 Log.w(TAG, "config_wifi_revert_country_code_on_cellular_loss is set, " 66 + "but there is no default country code."); 67 mRevertCountryCodeOnCellularLoss = false; 68 } 69 } 70 71 Log.d(TAG, "mDefaultCountryCode " + mDefaultCountryCode 72 + " mRevertCountryCodeOnCellularLoss " + mRevertCountryCodeOnCellularLoss); 73 } 74 75 /** 76 * Enable verbose logging for WifiCountryCode. 77 */ enableVerboseLogging(int verbose)78 public void enableVerboseLogging(int verbose) { 79 if (verbose > 0) { 80 DBG = true; 81 } else { 82 DBG = false; 83 } 84 } 85 86 /** 87 * Change the state to indicates if wpa_supplicant is ready to handle country code changing 88 * request or not. 89 * We call native code to request country code changes only when wpa_supplicant is 90 * started but not yet L2 connected. 91 */ setReadyForChange(boolean ready)92 public synchronized void setReadyForChange(boolean ready) { 93 mReady = ready; 94 mReadyTimestamp = FORMATTER.format(new Date(System.currentTimeMillis())); 95 // We are ready to set country code now. 96 // We need to post pending country code request. 97 if (mReady) { 98 updateCountryCode(); 99 } 100 } 101 102 /** 103 * Enable force-country-code mode 104 * @param countryCode The forced two-letter country code 105 */ enableForceCountryCode(String countryCode)106 synchronized void enableForceCountryCode(String countryCode) { 107 if (TextUtils.isEmpty(countryCode)) { 108 Log.d(TAG, "Fail to force country code because the received country code is empty"); 109 return; 110 } 111 mForceCountryCode = true; 112 mTelephonyCountryCode = countryCode.toUpperCase(Locale.US); 113 // If wpa_supplicant is ready we set the country code now, otherwise it will be 114 // set once wpa_supplicant is ready. 115 if (mReady) { 116 updateCountryCode(); 117 } else { 118 Log.d(TAG, "skip update supplicant not ready yet"); 119 } 120 } 121 122 /** 123 * Disable force-country-code mode 124 */ disableForceCountryCode()125 synchronized void disableForceCountryCode() { 126 mForceCountryCode = false; 127 // Set mTelephonyCountryCode to null so that default country code is used until 128 // next call of setCountryCode(). 129 mTelephonyCountryCode = null; 130 } 131 132 /** 133 * Handle country code change request. 134 * @param countryCode The country code intended to set. 135 * This is supposed to be from Telephony service. 136 * otherwise we think it is from other applications. 137 * @return Returns true if the country code passed in is acceptable. 138 */ setCountryCode(String countryCode)139 public synchronized boolean setCountryCode(String countryCode) { 140 if (mForceCountryCode) { 141 Log.d(TAG, "Country code can't be set because it is the force-country-code mode"); 142 return false; 143 } 144 Log.d(TAG, "Receive set country code request: " + countryCode); 145 mTelephonyCountryTimestamp = FORMATTER.format(new Date(System.currentTimeMillis())); 146 147 // Empty country code. 148 if (TextUtils.isEmpty(countryCode)) { 149 if (mRevertCountryCodeOnCellularLoss) { 150 Log.d(TAG, "Received empty country code, reset to default country code"); 151 mTelephonyCountryCode = null; 152 } 153 } else { 154 mTelephonyCountryCode = countryCode.toUpperCase(Locale.US); 155 } 156 // If wpa_supplicant is ready we set the country code now, otherwise it will be 157 // set once wpa_supplicant is ready. 158 if (mReady) { 159 updateCountryCode(); 160 } else { 161 Log.d(TAG, "skip update supplicant not ready yet"); 162 } 163 164 return true; 165 } 166 167 /** 168 * Method to get the Country Code that was sent to wpa_supplicant. 169 * 170 * @return Returns the local copy of the Country Code that was sent to the driver upon 171 * setReadyForChange(true). 172 * If wpa_supplicant was never started, this may be null even if a SIM reported a valid 173 * country code. 174 * Returns null if no Country Code was sent to driver. 175 */ getCountryCodeSentToDriver()176 public synchronized String getCountryCodeSentToDriver() { 177 return mDriverCountryCode; 178 } 179 180 /** 181 * Method to return the currently reported Country Code from the SIM or phone default setting. 182 * 183 * @return The currently reported Country Code from the SIM. If there is no Country Code 184 * reported from SIM, a phone default Country Code will be returned. 185 * Returns null when there is no Country Code available. 186 */ getCountryCode()187 public synchronized String getCountryCode() { 188 return pickCountryCode(); 189 } 190 191 /** 192 * Method to dump the current state of this WifiCounrtyCode object. 193 */ dump(FileDescriptor fd, PrintWriter pw, String[] args)194 public synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 195 196 pw.println("mRevertCountryCodeOnCellularLoss: " + mRevertCountryCodeOnCellularLoss); 197 pw.println("mDefaultCountryCode: " + mDefaultCountryCode); 198 pw.println("mDriverCountryCode: " + mDriverCountryCode); 199 pw.println("mTelephonyCountryCode: " + mTelephonyCountryCode); 200 pw.println("mTelephonyCountryTimestamp: " + mTelephonyCountryTimestamp); 201 pw.println("mDriverCountryTimestamp: " + mDriverCountryTimestamp); 202 pw.println("mReadyTimestamp: " + mReadyTimestamp); 203 pw.println("mReady: " + mReady); 204 } 205 updateCountryCode()206 private void updateCountryCode() { 207 String country = pickCountryCode(); 208 Log.d(TAG, "updateCountryCode to " + country); 209 210 // We do not check if the country code equals the current one. 211 // There are two reasons: 212 // 1. Wpa supplicant may silently modify the country code. 213 // 2. If Wifi restarted therefoere wpa_supplicant also restarted, 214 // the country code counld be reset to '00' by wpa_supplicant. 215 if (country != null) { 216 setCountryCodeNative(country); 217 } 218 // We do not set country code if there is no candidate. This is reasonable 219 // because wpa_supplicant usually starts with an international safe country 220 // code setting: '00'. 221 } 222 pickCountryCode()223 private String pickCountryCode() { 224 if (mTelephonyCountryCode != null) { 225 return mTelephonyCountryCode; 226 } 227 if (mDefaultCountryCode != null) { 228 return mDefaultCountryCode; 229 } 230 // If there is no candidate country code we will return null. 231 return null; 232 } 233 setCountryCodeNative(String country)234 private boolean setCountryCodeNative(String country) { 235 mDriverCountryTimestamp = FORMATTER.format(new Date(System.currentTimeMillis())); 236 if (mWifiNative.setCountryCode(mWifiNative.getClientInterfaceName(), country)) { 237 Log.d(TAG, "Succeeded to set country code to: " + country); 238 mDriverCountryCode = country; 239 return true; 240 } 241 Log.d(TAG, "Failed to set country code to: " + country); 242 return false; 243 } 244 } 245 246