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