1 /* 2 * Copyright (C) 2017 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.annotation.NonNull; 20 import android.content.BroadcastReceiver; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.IntentFilter; 24 import android.database.ContentObserver; 25 import android.net.Uri; 26 import android.net.wifi.EAPConstants; 27 import android.net.wifi.WifiEnterpriseConfig; 28 import android.os.Handler; 29 import android.os.Looper; 30 import android.os.PersistableBundle; 31 import android.telephony.CarrierConfigManager; 32 import android.telephony.ImsiEncryptionInfo; 33 import android.telephony.SubscriptionInfo; 34 import android.telephony.SubscriptionManager; 35 import android.telephony.TelephonyManager; 36 import android.util.Base64; 37 import android.util.Log; 38 39 import java.io.FileDescriptor; 40 import java.io.PrintWriter; 41 import java.util.Arrays; 42 import java.util.HashMap; 43 import java.util.List; 44 import java.util.Map; 45 46 /** 47 * Class for maintaining/caching carrier Wi-Fi network configurations. 48 */ 49 public class CarrierNetworkConfig { 50 private static final String TAG = "CarrierNetworkConfig"; 51 52 private static final String NETWORK_CONFIG_SEPARATOR = ","; 53 private static final int ENCODED_SSID_INDEX = 0; 54 private static final int EAP_TYPE_INDEX = 1; 55 private static final int CONFIG_ELEMENT_SIZE = 2; 56 57 private static final Uri CONTENT_URI = Uri.parse("content://carrier_information/carrier"); 58 59 private boolean mDbg = false; 60 61 private final Map<String, NetworkInfo> mCarrierNetworkMap; 62 private boolean mIsCarrierImsiEncryptionInfoAvailable = false; 63 private ImsiEncryptionInfo mLastImsiEncryptionInfo = null; // used for dumpsys only 64 65 /** 66 * Enable/disable verbose logging. 67 */ enableVerboseLogging(int verbose)68 public void enableVerboseLogging(int verbose) { 69 mDbg = verbose > 0; 70 } 71 CarrierNetworkConfig(@onNull Context context, @NonNull Looper looper, @NonNull FrameworkFacade framework)72 public CarrierNetworkConfig(@NonNull Context context, @NonNull Looper looper, 73 @NonNull FrameworkFacade framework) { 74 mCarrierNetworkMap = new HashMap<>(); 75 updateNetworkConfig(context); 76 77 // Monitor for carrier config changes. 78 IntentFilter filter = new IntentFilter(); 79 filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); 80 context.registerReceiver(new BroadcastReceiver() { 81 @Override 82 public void onReceive(Context context, Intent intent) { 83 updateNetworkConfig(context); 84 } 85 }, filter); 86 87 framework.registerContentObserver(context, CONTENT_URI, false, 88 new ContentObserver(new Handler(looper)) { 89 @Override 90 public void onChange(boolean selfChange) { 91 updateNetworkConfig(context); 92 } 93 }); 94 } 95 96 /** 97 * @return true if the given SSID is associated with a carrier network 98 */ isCarrierNetwork(String ssid)99 public boolean isCarrierNetwork(String ssid) { 100 return mCarrierNetworkMap.containsKey(ssid); 101 } 102 103 /** 104 * @return the EAP type associated with a carrier AP, or -1 if the specified AP 105 * is not associated with a carrier network 106 */ getNetworkEapType(String ssid)107 public int getNetworkEapType(String ssid) { 108 NetworkInfo info = mCarrierNetworkMap.get(ssid); 109 return info == null ? -1 : info.mEapType; 110 } 111 112 /** 113 * @return the name of carrier associated with a carrier AP, or null if the specified AP 114 * is not associated with a carrier network. 115 */ getCarrierName(String ssid)116 public String getCarrierName(String ssid) { 117 NetworkInfo info = mCarrierNetworkMap.get(ssid); 118 return info == null ? null : info.mCarrierName; 119 } 120 121 /** 122 * @return True if carrier IMSI encryption info is available, False otherwise. 123 */ isCarrierEncryptionInfoAvailable()124 public boolean isCarrierEncryptionInfoAvailable() { 125 return mIsCarrierImsiEncryptionInfoAvailable; 126 } 127 128 /** 129 * Verify whether carrier IMSI encryption info is available. 130 * 131 * @param context Current application context 132 * 133 * @return True if carrier IMSI encryption info is available, False otherwise. 134 */ verifyCarrierImsiEncryptionInfoIsAvailable(Context context)135 private boolean verifyCarrierImsiEncryptionInfoIsAvailable(Context context) { 136 // TODO(b/132188983): Inject this using WifiInjector 137 TelephonyManager telephonyManager = 138 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); 139 if (telephonyManager == null) { 140 return false; 141 } 142 try { 143 mLastImsiEncryptionInfo = telephonyManager 144 .createForSubscriptionId(SubscriptionManager.getDefaultDataSubscriptionId()) 145 .getCarrierInfoForImsiEncryption(TelephonyManager.KEY_TYPE_WLAN); 146 if (mLastImsiEncryptionInfo == null) { 147 return false; 148 } 149 } catch (RuntimeException e) { 150 Log.e(TAG, "Failed to get imsi encryption info: " + e.getMessage()); 151 return false; 152 } 153 154 return true; 155 } 156 157 /** 158 * Utility class for storing carrier network information. 159 */ 160 private static class NetworkInfo { 161 final int mEapType; 162 final String mCarrierName; 163 NetworkInfo(int eapType, String carrierName)164 NetworkInfo(int eapType, String carrierName) { 165 mEapType = eapType; 166 mCarrierName = carrierName; 167 } 168 169 @Override toString()170 public String toString() { 171 return new StringBuffer("NetworkInfo: eap=").append(mEapType).append( 172 ", carrier=").append(mCarrierName).toString(); 173 } 174 } 175 176 /** 177 * Update the carrier network map based on the current carrier configuration of the active 178 * subscriptions. 179 * 180 * @param context Current application context 181 */ updateNetworkConfig(Context context)182 private void updateNetworkConfig(Context context) { 183 mIsCarrierImsiEncryptionInfoAvailable = verifyCarrierImsiEncryptionInfoIsAvailable(context); 184 185 // Reset network map. 186 mCarrierNetworkMap.clear(); 187 188 CarrierConfigManager carrierConfigManager = 189 (CarrierConfigManager) context.getSystemService(Context.CARRIER_CONFIG_SERVICE); 190 if (carrierConfigManager == null) { 191 return; 192 } 193 194 SubscriptionManager subscriptionManager = (SubscriptionManager) context.getSystemService( 195 Context.TELEPHONY_SUBSCRIPTION_SERVICE); 196 if (subscriptionManager == null) { 197 return; 198 } 199 List<SubscriptionInfo> subInfoList = subscriptionManager.getActiveSubscriptionInfoList(); 200 if (subInfoList == null) { 201 return; 202 } 203 204 // Process the carrier config for each active subscription. 205 for (SubscriptionInfo subInfo : subInfoList) { 206 CharSequence displayNameCs = subInfo.getDisplayName(); 207 String displayNameStr = displayNameCs == null ? "" : displayNameCs.toString(); 208 PersistableBundle bundle = carrierConfigManager.getConfigForSubId( 209 subInfo.getSubscriptionId()); 210 processNetworkConfig(bundle, displayNameStr); 211 } 212 } 213 214 /** 215 * Process the carrier network config, the network config string is formatted as follow: 216 * 217 * "[Base64 Encoded SSID],[EAP Type]" 218 * Where EAP Type is the standard EAP method number, refer to 219 * http://www.iana.org/assignments/eap-numbers/eap-numbers.xhtml for more info. 220 221 * @param carrierConfig The bundle containing the carrier configuration 222 * @param carrierName The display name of the associated carrier 223 */ processNetworkConfig(PersistableBundle carrierConfig, String carrierName)224 private void processNetworkConfig(PersistableBundle carrierConfig, String carrierName) { 225 if (carrierConfig == null) { 226 return; 227 } 228 String[] networkConfigs = carrierConfig.getStringArray( 229 CarrierConfigManager.KEY_CARRIER_WIFI_STRING_ARRAY); 230 if (mDbg) { 231 Log.v(TAG, "processNetworkConfig: networkConfigs=" 232 + Arrays.deepToString(networkConfigs)); 233 } 234 if (networkConfigs == null) { 235 return; 236 } 237 238 for (String networkConfig : networkConfigs) { 239 String[] configArr = networkConfig.split(NETWORK_CONFIG_SEPARATOR); 240 if (configArr.length != CONFIG_ELEMENT_SIZE) { 241 Log.e(TAG, "Ignore invalid config: " + networkConfig); 242 continue; 243 } 244 try { 245 246 String ssid = new String(Base64.decode( 247 configArr[ENCODED_SSID_INDEX], Base64.NO_WRAP)); 248 int eapType = parseEapType(Integer.parseInt(configArr[EAP_TYPE_INDEX])); 249 250 // Verify EAP type, must be a SIM based EAP type. 251 if (eapType == -1) { 252 Log.e(TAG, "Invalid EAP type: " + configArr[EAP_TYPE_INDEX]); 253 continue; 254 } 255 mCarrierNetworkMap.put(ssid, new NetworkInfo(eapType, carrierName)); 256 } catch (NumberFormatException e) { 257 Log.e(TAG, "Failed to parse EAP type: '" + configArr[EAP_TYPE_INDEX] + "' " 258 + e.getMessage()); 259 } catch (IllegalArgumentException e) { 260 Log.e(TAG, "Failed to decode SSID: '" + configArr[ENCODED_SSID_INDEX] + "' " 261 + e.getMessage()); 262 } 263 } 264 } 265 266 /** 267 * Convert a standard SIM-based EAP type (SIM, AKA, AKA') to the internal EAP type as defined in 268 * {@link WifiEnterpriseConfig.Eap}. -1 will be returned if the given EAP type is not 269 * SIM-based. 270 * 271 * @return SIM-based EAP type as defined in {@link WifiEnterpriseConfig.Eap}, or -1 if not 272 * SIM-based EAP type 273 */ parseEapType(int eapType)274 private static int parseEapType(int eapType) { 275 if (eapType == EAPConstants.EAP_SIM) { 276 return WifiEnterpriseConfig.Eap.SIM; 277 } else if (eapType == EAPConstants.EAP_AKA) { 278 return WifiEnterpriseConfig.Eap.AKA; 279 } else if (eapType == EAPConstants.EAP_AKA_PRIME) { 280 return WifiEnterpriseConfig.Eap.AKA_PRIME; 281 } 282 return -1; 283 } 284 285 /** Dump state. */ dump(FileDescriptor fd, PrintWriter pw, String[] args)286 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 287 pw.println(TAG + ": "); 288 pw.println("mCarrierNetworkMap=" + mCarrierNetworkMap); 289 pw.println("mIsCarrierImsiEncryptionInfoAvailable=" 290 + mIsCarrierImsiEncryptionInfoAvailable); 291 pw.println("mLastImsiEncryptionInfo=" + mLastImsiEncryptionInfo); 292 } 293 } 294